Skip to content

Instantly share code, notes, and snippets.

@Ash-Bash
Created April 7, 2020 14:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Ash-Bash/93fd55d89c1e36f592d3868f6b29b259 to your computer and use it in GitHub Desktop.
Save Ash-Bash/93fd55d89c1e36f592d3868f6b29b259 to your computer and use it in GitHub Desktop.
Creates a UIViewController with Blurred Background Which acts like a UIHostingController for SwiftUI Views (Useful for UIKit developers using SwiftUI Views for Modal Views)
//
// BluredBackgroundHostingController.swift
// v1.0.1
//
// Created by Ashley Chapman on 07/04/2020.
// Copyright © 2020 Ashley Chapman. All rights reserved.
//
import UIKit
class BlurredHostingController: UIViewController {
// Variables
var hostingController: UIViewController!
var blurEffect: UIBlurEffect.Style = .prominent
var translucentEffect: TranslucentEffect = .regular
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = nil
if (translucentEffect != .opaque) {
let blurEffect = UIBlurEffect(style: self.blurEffect)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = self.view.frame
self.view.addSubview(blurEffectView)
NSLayoutConstraint.activate([
blurEffectView.widthAnchor.constraint(equalTo: view.widthAnchor),
blurEffectView.heightAnchor.constraint(equalTo: view.heightAnchor),
blurEffectView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
blurEffectView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
self.addChild(hostingController)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
hostingController.view.alpha = translucent()
hostingController.view.backgroundColor = nil
self.view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
NSLayoutConstraint.activate([
hostingController.view.widthAnchor.constraint(equalTo: view.widthAnchor),
hostingController.view.heightAnchor.constraint(equalTo: view.heightAnchor),
hostingController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
hostingController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
private func translucent() -> CGFloat {
if (translucentEffect == .opaque) {
return 1.0
} else if (translucentEffect == .ultrathick) {
return 0.95
} else if (translucentEffect == .thick) {
return 0.9
} else if (translucentEffect == .regular) {
return 0.85
} else if (translucentEffect == .prominent) {
return 0.8
} else if (translucentEffect == .thin) {
return 0.75
} else if (translucentEffect == .ultrathin) {
return 0.7
} else {
return 1.0
}
}
}
enum TranslucentEffect {
case opaque
case ultrathick
case thick
case regular
case prominent
case thin
case ultrathin
}
@Ash-Bash
Copy link
Author

Ash-Bash commented Apr 7, 2020

This Script is very hacky and dirty way to do it but works with great success
Here is a working example:

// Initialises BlurredHostingController
var blurredHostingController = BlurredHostingController()

// Sets the Hosting View for the SwiftUI View Logic
blurredHostingController.hostingController = UIHostingController(rootView: ContentView())

// Blur Tweaks for blurredHostingController
blurredHostingController.blurEffect = .systemMaterial
blurredHostingController.translucentEffect = .ultrathin

// Presents View Controller as a Modal View Controller
self.present(blurredHostingController animated: true, completion: nil)

@ablyes
Copy link

ablyes commented Apr 14, 2020

Hi,

I wanted to use this code for coding Today Extentions (widget) that should use SwiftUI views.

My code :
`import UIKit
import NotificationCenter
import SwiftUI

class TodayViewController: UIViewController, NCWidgetProviding {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
}
    
@IBSegueAction func addSwiftUIView(_ coder: NSCoder) -> UIViewController? {
    // Initialises BlurredHostingController
    let blurredHostingController = BlurredHostingController()

    // Sets the Hosting View for the SwiftUI View Logic
    blurredHostingController.hostingController = UIHostingController(coder: coder, rootView: WidgetMainView().environmentObject(ChronoState()))

    // Blur Tweaks for blurredHostingController
    blurredHostingController.blurEffect = .systemMaterial
    blurredHostingController.translucentEffect = .ultrathin
    

    // Presents View Controller as a Modal View Controller
    self.present(blurredHostingController, animated: true, completion: nil)
    
    return blurredHostingController
}
func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
    // Perform any setup necessary in order to update the view.
    
    // If an error is encountered, use NCUpdateResult.Failed
    // If there's no update required, use NCUpdateResult.NoData
    // If there's an update, use NCUpdateResult.NewData
    
    completionHandler(NCUpdateResult.newData)
}

}`

Before, the addSwiftUIView was returning an UIHostingController

return UIHostingController(coder: coder, rootView: WidgetMainView().environmentObject(ChronoState()))

Error:
2020-04-14 21:00:41.228693+0200 Chrono Training Widget Extension[6695:1479245] Warning: Attempt to present <Chrono_Training_Widget_Extension.BlurredHostingController: 0x15b1068d0> on <Chrono_Training_Widget_Extension.TodayViewController: 0x159d06920> whose view is not in the window hierarchy! 2020-04-14 21:00:41.229276+0200 Chrono Training Widget Extension[6695:1479245] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'This coder requires that replaced objects be returned from initWithCoder:' *** First throw call stack: (0x18fe24164 0x18fb38c1c 0x18fd13bb8 0x19348b984 0x193426d50 0x193bed668 0x19348b93c 0x19348bb5c 0x193426d50 0x193be8638 0x193beb200 0x1940d9ca4 0x1940d9b3c 0x1940da924 0x1940dab78 0x1940dae78 0x193902d80 0x19390330c 0x1a43e3928 0x1938fe36c 0x193902f20 0x1a43e331c 0x194331bb0 0x18fe2ad14 0x18fcf3de8 0x18fcf4a10 0x19433af44 0x18fe285d4 0x18fe2ab60 0x18fe2ad14 0x18fcf3de8 0x18fcf4a10 0x194322ab0 0x18fe285d4 0x18fe2ab60 0x18fe2ad14 0x18fcf3de8 0x18fcf4a10 0x18fe285d4 0x18fe2ab60 0x18fe2ad14 0x18fcf3de8 0x18fac433c 0x18fac70d4 0x1950d12b4 0x1950d0f60 0x1950d14cc 0x18fd9f860 0x18fd9f7b4 0x18fd9ef04 0x18fd99ca4 0x18fd99660 0x19a1aa604 0x193f6e15c 0x18fa37fd4 0x18fa22c48 0x18fa253ac 0x190120768 0x19d60fb04 0x19d60f7f8 0x19d60ff24 0x19030bfa0 0x18fc151ec) libc++abi.dylib: terminating with uncaught exception of type NSException

I don't know why the need to call initWithCoder ?

@Ash-Bash
Copy link
Author

Ash-Bash commented Apr 14, 2020

Hi,

I wanted to use this code for coding Today Extentions (widget) that should use SwiftUI views.

My code :
`import UIKit
import NotificationCenter
import SwiftUI

class TodayViewController: UIViewController, NCWidgetProviding {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
}
    
@IBSegueAction func addSwiftUIView(_ coder: NSCoder) -> UIViewController? {
    // Initialises BlurredHostingController
    let blurredHostingController = BlurredHostingController()

    // Sets the Hosting View for the SwiftUI View Logic
    blurredHostingController.hostingController = UIHostingController(coder: coder, rootView: WidgetMainView().environmentObject(ChronoState()))

    // Blur Tweaks for blurredHostingController
    blurredHostingController.blurEffect = .systemMaterial
    blurredHostingController.translucentEffect = .ultrathin
    

    // Presents View Controller as a Modal View Controller
    self.present(blurredHostingController, animated: true, completion: nil)
    
    return blurredHostingController
    // ORIGINAL

// return UIHostingController(coder: coder, rootView: WidgetMainView().environmentObject(ChronoState()))
}
func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.

    // If an error is encountered, use NCUpdateResult.Failed
    // If there's no update required, use NCUpdateResult.NoData
    // If there's an update, use NCUpdateResult.NewData
    
    completionHandler(NCUpdateResult.newData)
}

}
`

Error:
2020-04-14 21:00:41.228693+0200 Chrono Training Widget Extension[6695:1479245] Warning: Attempt to present <Chrono_Training_Widget_Extension.BlurredHostingController: 0x15b1068d0> on <Chrono_Training_Widget_Extension.TodayViewController: 0x159d06920> whose view is not in the window hierarchy! 2020-04-14 21:00:41.229276+0200 Chrono Training Widget Extension[6695:1479245] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'This coder requires that replaced objects be returned from initWithCoder:' *** First throw call stack: (0x18fe24164 0x18fb38c1c 0x18fd13bb8 0x19348b984 0x193426d50 0x193bed668 0x19348b93c 0x19348bb5c 0x193426d50 0x193be8638 0x193beb200 0x1940d9ca4 0x1940d9b3c 0x1940da924 0x1940dab78 0x1940dae78 0x193902d80 0x19390330c 0x1a43e3928 0x1938fe36c 0x193902f20 0x1a43e331c 0x194331bb0 0x18fe2ad14 0x18fcf3de8 0x18fcf4a10 0x19433af44 0x18fe285d4 0x18fe2ab60 0x18fe2ad14 0x18fcf3de8 0x18fcf4a10 0x194322ab0 0x18fe285d4 0x18fe2ab60 0x18fe2ad14 0x18fcf3de8 0x18fcf4a10 0x18fe285d4 0x18fe2ab60 0x18fe2ad14 0x18fcf3de8 0x18fac433c 0x18fac70d4 0x1950d12b4 0x1950d0f60 0x1950d14cc 0x18fd9f860 0x18fd9f7b4 0x18fd9ef04 0x18fd99ca4 0x18fd99660 0x19a1aa604 0x193f6e15c 0x18fa37fd4 0x18fa22c48 0x18fa253ac 0x190120768 0x19d60fb04 0x19d60f7f8 0x19d60ff24 0x19030bfa0 0x18fc151ec) libc++abi.dylib: terminating with uncaught exception of type NSException

I don't know why the need to call initWithCoder ?

I dont know why it would since its just a UIViewController that is a container for the SwiftUI, nothing special about it, but what I can say is that Ive only used UIHostingControllers in the addSwiftUIView() but I'd suggest add a coder in the BlurredHostingController() inside addSwiftUIView() like this BlurredHostingController(coder: coder) see if that works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment