交互式弹出手势识别器应允许用户在滑动超过一半屏幕(或这些线周围的某些内容)时返回导航堆栈中的上一个 View 。在 SwiftUI 中,当滑动距离不够远时,手势不会被取消。
SwiftUI: /image/shQl8.jpg
UIKit: /image/2EeIS.jpg
<小时/>问题:
使用 SwiftUI View 时是否可以获取 UIKit 行为?
<小时/>尝试
我尝试将 UIHostingController 嵌入 UINavigationController 中,但这提供了与 NavigationView 完全相同的行为。
struct ContentView: View {
var body: some View {
UIKitNavigationView {
VStack {
NavigationLink(destination: Text("Detail")) {
Text("SwiftUI")
}
}.navigationBarTitle("SwiftUI", displayMode: .inline)
}.edgesIgnoringSafeArea(.top)
}
}
struct UIKitNavigationView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UINavigationController {
let host = UIHostingController(rootView: content())
let nvc = UINavigationController(rootViewController: host)
return nvc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}
最佳答案
我最终覆盖了默认的 NavigationView
和 NavigationLink
以获得所需的行为。这看起来很简单,我一定忽略了默认 SwiftUI View 所做的事情?
导航 View
我将 UINavigationController
包装在一个 super 简单的 UIViewControllerRepresentable
中,它将 UINavigationController
作为环境对象提供给 SwiftUI 内容 View 。这意味着 NavigationLink
稍后可以获取它,只要它位于同一个导航 Controller 中(呈现的 View Controller 不接收环境对象),这正是我们想要的。
注意: NavigationView 需要 .edgesIgnoringSafeArea(.top)
并且我还不知道如何在结构本身中设置它。如果您的 NVC 在顶部切断,请参阅示例。
struct NavigationView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UINavigationController {
let nvc = UINavigationController()
let host = UIHostingController(rootView: content().environmentObject(nvc))
nvc.viewControllers = [host]
return nvc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}
extension UINavigationController: ObservableObject {}
导航链接
我创建了一个自定义的 NavigationLink,它访问环境 UINavigationController 以推送托管下一个 View 的 UIHostingController。
注意:我没有实现 SwiftUI.NavigationLink 的 selection
和 isActive
,因为我不完全理解它们的含义还没有做。如果您想提供帮助,请发表评论/编辑。
struct NavigationLink<Destination: View, Label:View>: View {
var destination: Destination
var label: () -> Label
public init(destination: Destination, @ViewBuilder label: @escaping () -> Label) {
self.destination = destination
self.label = label
}
/// If this crashes, make sure you wrapped the NavigationLink in a NavigationView
@EnvironmentObject var nvc: UINavigationController
var body: some View {
Button(action: {
let rootView = self.destination.environmentObject(self.nvc)
let hosted = UIHostingController(rootView: rootView)
self.nvc.pushViewController(hosted, animated: true)
}, label: label)
}
}
这解决了向后滑动在 SwiftUI 上无法正常工作的问题,并且因为我使用名称 NavigationView 和 NavigationLink,所以我的整个项目立即切换到这些名称。
示例
在示例中,我也展示了模式表示。
struct ContentView: View {
@State var isPresented = false
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 30) {
NavigationLink(destination: Text("Detail"), label: {
Text("Show detail")
})
Button(action: {
self.isPresented.toggle()
}, label: {
Text("Show modal")
})
}
.navigationBarTitle("SwiftUI")
}
.edgesIgnoringSafeArea(.top)
.sheet(isPresented: $isPresented) {
Modal()
}
}
}
struct Modal: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 30) {
NavigationLink(destination: Text("Detail"), label: {
Text("Show detail")
})
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Dismiss modal")
})
}
.navigationBarTitle("Modal")
}
}
}
<小时/>
编辑:我一开始就说“这看起来很简单,我一定是忽略了一些东西”,我想我找到了它。这似乎没有将环境对象转移到下一个 View 。我不知道默认的 NavigationLink 是如何做到这一点的,所以现在我手动将对象发送到我需要它们的下一个 View 。
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
编辑2:
这通过执行 @EnvironmentObject var nvc: UINavigationController
将导航 Controller 公开给 NavigationView
内的所有 View 。解决这个问题的方法是使我们用来管理导航的environmentObject成为一个文件私有(private)类。我在要点中修复了这个问题:https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb
关于ios - 如何在 SwiftUI 中恢复与 UIKit (interactivePopGestureRecognizer) 中相同的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58234142/