skip to Main Content

I have a UiKit app using a UIHostingController to show a SwiftUI view. In this view there is a button, which I want to use to navigate to another ViewController. How can I push a new viewController from inside this SwiftUI view? (or more generally: how can I call a function from my ViewController from inside the SwiftUI view?)

Of course I could just NavigationView inside SwiftUI, but I want to do most with UiKit, in order to have clean separation between business logic and UI.


import UIKit
import SwiftUI

class MainViewController: UIViewController {

    var viewModel = ViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addView()
    }
    
    func addView() {
            let testView = SwiftUIView(viewModel: viewModel)
            let controller = UIHostingController(rootView: testView)
            addChild(controller)
            controller.view.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(controller.view)
            controller.didMove(toParent: self)

            NSLayoutConstraint.activate([
                controller.view.widthAnchor.constraint(equalTo: view.widthAnchor),
                controller.view.heightAnchor.constraint(equalTo: view.heightAnchor),
                controller.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                controller.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
            ])
        }

    func navigate() { // how do I call this from inside SwiftUI testView view?
        let otherView = Text("test")
        let nextVc = UIHostingController(rootView: otherView)
        navigationController?.pushViewController(nextVc, animated: true)
    }
}

with the SwiftUI being something like:

import SwiftUI

struct SwiftUIView: View {
    
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
         Button {
                    // push new view controller
                } label: {
                    Text("go")
                }
    }
}

2

Answers


  1. call this function on your button-click event. try to make this function global so you can access it from every swiftUI View.

    func navigateToViewController(view : any View)
    {
        let newVC = UIHostingController(rootView: view)
        newVC.modalPresentationStyle = .overCurrentContext
        let transition = CATransition()
        transition.duration = 0.3
        transition.type = CATransitionType.push
        transition.subtype = CATransitionSubtype.fromRight
        transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        UIApplication.shared.windows[0].rootViewController!.view.window!.layer.add(transition, forKey: kCATransition)
        UIApplication.shared.windows[0].rootViewController!.present(newVC, animated: false, completion: nil)
    }
    
    
    Login or Signup to reply.
  2. Make your MainViewController as ObservableObject

    Set environment object on testView

    Access your environment object inside your view using @EnvironmentObject var parent: MainViewController

    class MainViewController: UIViewController, ObservableObject {
    
        var viewModel = ViewModel()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            addView()
        }
        
        func addView() {
                let testView = SwiftUIView(viewModel: viewModel)
                let controller = UIHostingController(rootView: testView.environmentObject(self))
                addChild(controller)
                controller.view.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(controller.view)
                controller.didMove(toParent: self)
    
                NSLayoutConstraint.activate([
                    controller.view.widthAnchor.constraint(equalTo: view.widthAnchor),
                    controller.view.heightAnchor.constraint(equalTo: view.heightAnchor),
                    controller.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                    controller.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
                ])
            }
    }
    
    struct SwiftUIView: View {
        @EnvironmentObject var parent: MainViewController
        var body: some View {
            Button {
                // push new view controller
                parent.navigationController?.pushViewController(<#T##viewController: UIViewController##UIViewController#>, animated: <#T##Bool#>)
            } label: {
                Text("go")
            }
        }
        
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search