Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Navigating from SwiftUI View to UIKit UIViewController

As of now, I have an application built entirely using UIKit. However, I wish to be able to start implementing some SwiftUI Views to replace some UIViewControllers.

I've been able to do this to navigate from the UIViewController to SwiftUI View on button tap:

    @IBAction func buttonTapped(_ sender: Any) {
        let newView = UIHostingController(rootView: SwiftUIView(viewObj: self.view, sb: self.storyboard, dismiss: self.dismiss) )
        view.window?.rootViewController = newView
        view.window?.makeKeyAndVisible()
    }

My question is, how would I transition from a single SwiftUI View to a UIViewController?(Since the rest of the application is in UIKit)? I've got a button in the SwiftUI View, to navigate back to the UIViewController on tap. I've tried:

  1. Passing the view and storyboard objects to the SwiftUI View, then calling doing something similar to the code above to change the current view controller. However, when tried on the simulator nothing happens.
  2. Using .present to show the SwiftUI View modally. This works and I can allow the SwiftUI View to .dismiss itself. However, this only works modally, and I hope to make this work properly (i.e change screen)

Here is my simple SwiftUI View:

struct SwiftUIView: View {
    var viewObj:UIView? // Perhaps use this for transition back?
    var sb:UIStoryboard?
    var dismiss: (() -> Void)?

    var body: some View {

        Button(action: {
            // Do something here to Transition
        }) {
            Text("This is a SwiftUI view.")
        }
    }
}

I'm having trouble understanding how to properly integrate SwiftUI into UIKit NOT the other way around, and I'm not sure if UIViewControllerRepresentable is the answer to this. Any solution to this, alternatives or helpful knowledge is very much appreciated. Thanks again!

like image 786
Chiah Soon Avatar asked Feb 19 '20 18:02

Chiah Soon


People also ask

How do I navigate from SwiftUI to UIKit?

To go back from the SwiftUI screen, we need to add a UINavigationController and pass our root navigation controller to the SwiftUI screen. We pushed to the SwiftUI screen before using the UIHostingController . Now, we will use the UINavigationController to pop back to the UIKit screen.

Can you use a SwiftUI view in UIKit?

SwiftUI works seamlessly with the existing UI frameworks on all Apple platforms. For example, you can place UIKit views and view controllers inside SwiftUI views, and vice versa.

How do I transfer data from SwiftUI to UIKit?

First, use binding to your input view. And for action use closure to get action from SwiftUI to UIKit. Here is a possible solution. override func viewDidLoad() { super.

How do I add a SwiftUI view to UIKit view?

Adding a SwiftUI view to a UIKit view First, we add the hosting controller as a child to the current view controller. The view is added to the view hierarchy of the current view controller. Constraints are set up in code to update the boundaries of our SwiftUI view.


3 Answers

Ciao,

I tried to follow your approach by using closure callbacks.

struct SwiftUIView: View {
    var dismiss: (() -> Void)?
    var present: (()->Void)?

    var body: some View {
        VStack(spacing: 20) {
            Button(action: {
                self.dismiss?()
            }) {
                Text("Dismiss me")
            }
            Button(action: {
                self.present?()
            }) {
                Text("Present some UIViewController")
            }
        }
    }
}

When you present your UIHostingController, you want to implement the 2 closure callbacks:

@IBAction func buttonTapped(_ sender: Any) {
    let hostingController = UIHostingController(rootView: SwiftUIView())
    hostingController.rootView.dismiss = {
        hostingController.dismiss(animated: true, completion: nil)
    }
    hostingController.rootView.present = {
        let destination = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "VC_TO_PRESENT")
        hostingController.present(destination, animated: true, completion: nil)
    }

    present(hostingController, animated: true, completion: nil)
}
like image 158
mrcfal Avatar answered Oct 19 '22 04:10

mrcfal


You can do the same, only in mirror order, like following (scratchy) ...

    Button(action: {
        if let vc = self.sb?.instantiateViewController(withIdentifier: "some_identifier") {
            self.viewObj?.window?.rootViewController = vc
            // or via present as alternate
            // self.viewObj?.window?.rootViewController.present(vc, animated: true, completion: nil)
        }
    }) {
like image 20
Asperi Avatar answered Oct 19 '22 05:10

Asperi


you can change the presentation style and transition style to present the controller full screen

let vc = UIHostingController(rootView: LoginView())
vc.modalPresentationStyle = .fullScreen
vc.modalTransitionStyle = .crossDissolve
self.present(vc, animated: true)
like image 1
Qaim Raza Avatar answered Oct 19 '22 03:10

Qaim Raza