Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dismiss a SwiftUI View that is contained in a UIHostingController

Tags:

ios

uikit

swiftui

I have rewritten my sign in view controller as a SwiftUI View. The SignInView is wrapped in a UIHostingController subclass (final class SignInViewController: UIHostingController<SignInView> {}), and is presented modally, full screen, when sign in is necessary.

Everything is working fine, except I can't figure out how to dismiss the SignInViewController from the SignInView. I have tried adding:

@Environment(\.isPresented) var isPresented 

in SignInView and assigning it to false when sign in is successful, but this doesn't appear to interop with UIKit. How can I dismiss the view?

like image 672
jjatie Avatar asked Jul 24 '19 20:07

jjatie


People also ask

How do I dismiss a SwiftUI view?

The alternate way to dismiss the screen is to use State/Binding variables. You present the screen on the current view using a State variable. Pass that state variable as a Binding variable to the next screen and attach the action that will toggle this binding variable.

How do I dismiss a presented Viewcontroller?

When it comes time to dismiss a presented view controller, the preferred approach is to let the presenting view controller dismiss it. In other words, whenever possible, the same view controller that presented the view controller should also take responsibility for dismissing it.

How do you dismiss a storyboard?

Select the button that should make the UIViewController Disappear and drag it to the UIViewController you want to go to. In my case it shows **dismiss Controller* because of the name of my Class. Select it and you are done!

What is uihostingcontroller in SwiftUI?

A UIKit view controller that manages a SwiftUI view hierarchy. Create a UIHostingController object when you want to integrate SwiftUI views into a UIKit view hierarchy.

Is it possible to swipe down to dismiss with uihostingcontroller?

However, when we use UIHostingController as the ViewController, the ability to swipe down to dismiss no longer works, for both Floating Modal and Full Screen. Even if I use UISwipeGestureRecognizer, it doesn’t get recognized, as the SwiftUI View is not the view that we have in code below, so no gesture will be captured.

How to use SwiftUI with UIKit?

You can use SwiftUI with UIKit very easily with the help of UIHostingController. Since UIKit does not speak SwiftUI, let’s introduce a mediator here, UIHostingController! UIHostingController creates a bridge between UIKit and SwiftUI framework. It works as a Container View for SwiftUI view, meaning you can use it as normal UIViewController.

How to have Swift UI view inside viewcontroller in SwiftUI?

Wrapping UIView or ViewController into SwiftUI is made easy with UIViewControllerRepresentable and UIViewRepresentable. Likewise, it is also possible to have SwiftUI View inside ViewController, by using UIHostingController. The official tutorial is here.


2 Answers

I found another approach that seems to work well and which feels a little cleaner than some of the other approaches. Steps:

  1. Add a dismissAction property to the SwiftUI view:
struct SettingsUIView: View {     var dismissAction: (() -> Void)     ... }     
  1. Call the dismissAction when you want to dismiss the view:
Button(action: dismissAction ) {     Text("Done") } 
  1. When you present the view, provide it with a dismissal handler:
let settingsView = SettingsUIView(dismissAction: {self.dismiss( animated: true, completion: nil )}) let settingsViewController = UIHostingController(rootView: settingsView )  present( settingsViewController, animated: true ) 
like image 67
Sean McMains Avatar answered Sep 23 '22 15:09

Sean McMains


UPDATE: From the release notes of iOS 15 beta 1:

isPresented, PresentationMode, and the new DismissAction action dismiss a hosting controller presented from UIKit. (52556186)


I ended up finding a much simpler solution than what was offered:

 final class SettingsViewController: UIHostingController<SettingsView> {     required init?(coder: NSCoder) {         super.init(coder: coder, rootView: SettingsView())         rootView.dismiss = dismiss     }      func dismiss() {         dismiss(animated: true, completion: nil)     } }  struct SettingsView: View {     var dismiss: (() -> Void)?          var body: some View {         NavigationView {             Form {                 Section {                     Button("Dimiss", action: dismiss!)                 }             }             .navigationBarTitle("Settings")         }     } } 
like image 25
jjatie Avatar answered Sep 24 '22 15:09

jjatie