从SwiftUI视图导航到UIKit的UIViewController的方法

11

截至现在,我有一个完全使用UIKit构建的应用程序。然而,我希望能够开始实现一些SwiftUI视图来替换一些UIViewControllers。

我已经能够通过按钮点击从UIViewController导航到SwiftUI视图:

    @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()
    }

我的问题是,我该如何从单个SwiftUI视图过渡到UIViewController?(因为应用程序的其余部分在UIKit中)? 我在SwiftUI View中有一个按钮,可以在点击时导航回UIViewController。 我尝试过:

  1. viewstoryboard对象传递给SwiftUI View,然后调用类似于以上代码的操作来更改当前视图控制器。 但是,在模拟器上尝试时没有任何反应。
  2. 使用.present以模态方式显示SwiftUI View。 这可以工作,并且我可以允许SwiftUI View .dismiss自身。 但是,这仅以模态方式工作,我希望使其正常工作(即更改屏幕)

这是我的简单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.")
        }
    }
}

我不太明白如何将SwiftUI正确地集成到UIKit中,而不是反过来,并且我不确定UIViewControllerRepresentable是否是解决方法。非常感谢您提供任何解决方案、替代方案或有用的知识。再次感谢!


1
嗨Chiah,我陷入了类似的困境。你能找到解决办法吗? - Ishmeet
嘿!不幸的是,我无法解决这个问题。由于我的应用程序相对较小,我只好在SwiftUI中重新编写了所有东西(说实话它还不错!)。很抱歉! - Chiah Soon
5个回答

10

你好,

我尝试使用闭包回调来按照您的方法进行操作。

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")
            }
        }
    }
}

当您展示UIHostingController时,您需要实现两个闭包回调:
@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)
}

同样地,我提到过尝试了这个并且在以 SwiftUI 视图模态方式呈现时可以工作,但如果我想要正确的转换(即非模态,覆盖整个屏幕且无法向下滑动),它就不起作用! - Chiah Soon

2

将您的UIViewController包装到UIViewControllerRepresentable包装器中。

struct LessonDetailsViewControllerWrapper: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIViewController

    func makeUIViewController(context: UIViewControllerRepresentableContext<LessonDetailsViewControllerWrapper>) -> UIViewController {
        let viewController = LessonDetailsViewController()
        return viewController
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<LessonDetailsViewControllerWrapper>) {}
}

然后从您的SwiftUI视图中使用导航链接导航:

  NavigationLink(destination: LessonDetailsViewControllerWrapper()) {
                            LessonRow(lesson: lesson)
                        }


1

您可以更改演示方式和转换方式以全屏呈现控制器。

let vc = UIHostingController(rootView: LoginView())
vc.modalPresentationStyle = .fullScreen
vc.modalTransitionStyle = .crossDissolve
self.present(vc, animated: true)

1
你可以按照镜像顺序做同样的事情,就像以下方式(刮痕)...
    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)
        }
    }) {

1
嗨,正如我所提到的,我尝试过这个(并且刚刚又尝试了一次),但是当按钮被点击时什么都没有发生! - Chiah Soon

0
这里是一个来自 UIKit 按钮按下的推入导航控制器示例。
let controller = UIHostingController(rootView: LoginViewController())
    self.navigationController?.pushViewController(controller, animated: true)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接