问题
I am developing an app using SwiftUI. The app is based around a NavigationView
.
I am using a third-part framework that provides UIKit
components and the framework has not been updated to support SwiftUI yet.
One framework method is expecting a parameter of type UINavigationController
How can I supply this framework the NavigationControlle
r created by SwiftUI ? Or how can I create a UINavigationController
that will replace SwiftUI's default ?
I read https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit and https://sarunw.com/posts/uikit-in-swiftui but these seems to address another question : they explain how to use UIKit components in a SwiftUI app. My problem is the other way around, I want to use SwiftUI App and access underlying NavigationController object.
回答1:
I don't think you can do that right now. Looking at the view debugger for NavigationView
I get the image below.
So it seems to you will have to go the other way around:
Start with a UINavigationController
, and wrap the SwiftUI view(s) in UIHostingController
.
回答2:
Thanks to Yonat's explanation I understood how to do this and here is my solution, hoping it will help others.
Part 1 : The UI View Controller that will be used from Swift UI. It calls a third-party authentication library, passing the UINavigationControler
as parameter. The UINavigationController
is an empty view, just there to allow the third-party authentication library to have a Navigation Controller to pop up the Login Screen.
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)
}
}
Part 2 : The Swift UI View is displaying the (empty) UINavigationControler and overlays a SwiftUI view on top of it.
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
// ...
}
}
}
}
回答3:
I tried to do the same thing because I wanted to make the interactivePopGestureRecognizer work on the whole view.
I managed to access the current navigation controller using an UINavigationController extension and overriding viewDidAppear, checking if the interactivePopGestureRecognizer was enabled and changed it ( https://stackoverflow.com/a/58068947/1745000)
At the end my effort was pointless. When the navigation view presented the DetailHostingController, it toggled off interactivePopGestureRecognizer.isEnabled!
The hosting view via topViewController.view does contain a gesture recogniser of private type SwiftUI.UIGestureRecognizer. No targets are set though...
Embedding a traditional UINavigationController may also be preferred because navigation view's own pop gesture isn't cancellable (if you drag the view a little bit and stop, it snaps back and then dismiss the detail view.
来源:https://stackoverflow.com/questions/58049846/swiftui-how-to-access-uinavigationcontroller-from-navigationview