SwiftUI: 如何从 NavigationView 访问 UINavigationController

26

我正在使用SwiftUI开发应用程序。该应用程序基于NavigationView

我正在使用一个第三方框架,该框架提供了UIKit组件,但该框架尚未更新以支持SwiftUI。

其中一个框架方法需要一个UINavigationController类型的参数

我该如何向这个框架提供由SwiftUI创建的NavigationController?或者我该如何创建一个可以替换SwiftUI默认导航控制器的UINavigationController

我阅读了https://developer.apple.com/tutorials/swiftui/interfacing-with-uikithttps://sarunw.com/posts/uikit-in-swiftui 但这些似乎是回答另一个问题:它们解释了如何在SwiftUI应用程序中使用UIKit组件,而我的问题则相反,我想使用SwiftUI应用程序并访问底层的NavigationController对象。

[更新] 实现我的解决方案的代码可从此工作坊获取:https://amplify-ios-workshop.go-aws.com/30_add_authentication/20_client_code.html#loginviewcontroller-swift


9
请不要因为你不理解这个问题就进行负评。这是一个合理的问题。 - Yonat
3个回答

12

感谢Yonat的解释,我明白了如何做到这一点,以下是我的解决方案,希望能帮助其他人。

Part 1:将在Swift UI中使用的UI View Controller。它调用第三方认证库,将UINavigationControler作为参数传递。 UINavigationController是一个空视图,只是为了让第三方认证库有一个导航控制器来弹出登录屏幕。

struct LoginViewController: UIViewControllerRepresentable {

    let navController =  UINavigationController()


    func makeUIViewController(context: Context) -> UINavigationController {
        navController.setNavigationBarHidden(true, animated: false)
        let viewController = UIViewController()
        navController.addChild(viewController)
        return navController
    }

    func updateUIViewController(_ pageViewController: UINavigationController, context: Context) {
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }

    class Coordinator: NSObject {
        var parent: LoginViewController

        init(_ loginViewController: LoginViewController) {
            self.parent = loginViewController
        }
    }

    func authenticate() {
        let app = UIApplication.shared.delegate as! AppDelegate
        let userData = app.userData

        userData.authenticateWithDropinUI(navigationController: navController)
    }

}

第二部分:SwiftUI视图正在显示(空)UINavigationController,并在其上方叠加了一个SwiftUI视图。

import SwiftUI

struct LandingView: View {
    @ObservedObject public var user : UserData

    var body: some View {

        let loginView = LoginViewController()

        return VStack {

            // .wrappedValue is used to extract the Bool from Binding<Bool> type
            if (!$user.isSignedIn.wrappedValue) {

                ZStack {
                    loginView
                    // build your welcome view here 
                    Button(action: { loginView.authenticate() } ) {
                        UserBadge().scaleEffect(0.5)
                    }
                }

            } else {

                // my main app view 
                // ...
            }
        }
    }
}

我可以问一下你是否正在使用 Snapchat 的登录工具包吗?因为我也遇到了同样的问题。 - MAlshehri
不是的,我正在使用AWS Amplify身份验证组件https://aws-amplify.github.io/docs/ios/authentication。我创建了一个具有逐步说明的研讨会,以展示iOS上的AWS Amplify https://amplify-ios-workshop.go-aws.com/。 - Sébastien Stormacq

10

我认为你现在无法这样做。查看NavigationView的视图调试器,我得到了下面的图片。

因此,似乎你需要采用另一种方式:
首先使用UINavigationController,然后在UIHostingController中包装SwiftUI视图。

输入图片说明


1
感谢您的回复和支持 :-) 我尝试了您的解决方案,并按照您的建议将手动管理的UINavigationController与Swift UI混合使用,现在已经有一些成果。我使用了这里解释的技术 https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit 。我需要优化和重构代码,并在几天内分享我的解决方案。 - Sébastien Stormacq
1
我意识到我从未发布过我的解决方案。它在这里发布:https://amplify-ios-workshop.go-aws.com/30_add_authentication/20_client_code.html#loginviewcontroller-swift - Sébastien Stormacq
这里有另一种可能的解决方案,适用于其他类型:SwiftUI-Introspect - Yonat
@SébastienStormacq 在你提供的链接中,我没有看到你如何访问 UINavigationController? - hyouuu
1
@hyouuu 我在这里解释了解决方案 https://dev59.com/GlMH5IYBdhLWcg3w1Dv0#58098217 - Sébastien Stormacq

0

我尝试做同样的事情,因为我想让interactivePopGestureRecognizer在整个视图上工作。

我使用了UINavigationController扩展并重写了viewDidAppear来访问当前导航控制器,检查是否启用了interactivePopGestureRecognizer并进行更改(https://dev59.com/hmEh5IYBdhLWcg3wlEVq#58068947

最终我的努力是徒劳的。当导航视图呈现DetailHostingController时,它切换了interactivePopGestureRecognizer.isEnabled!

通过topViewController.view嵌入传统的UINavigationController也可能更受欢迎,因为导航视图自己的弹出手势无法取消(如果您拖动视图一点并停止,则会弹回,然后关闭详细视图。


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