繁体   English   中英

SwiftUI 意外 NavigationLink 自动弹出

[英]SwiftUI Unexpectedly NavigationLink pops automatically

我有一个简单的用例,其中一个屏幕使用 NavigationLink 推送另一个屏幕。 iOS 14.5 有一个奇怪的行为,其中被推送的屏幕在被推送后立即弹出。

代码:

NavigationLink(destination: EmptyView()) { EmptyView()} 

我设法创建了一个示例应用程序,并在其中复制它。 我相信原因是@Environment(\.presentationMode)的存在似乎重新创建了视图,并导致推送的视图被弹出。

完全相同的代码在 Xcode 12 / iOS 14.4 中工作正常

从上周开始,我就陷入了困境。 为了解决这个问题,我只是将它添加到包含我现有NavigationLink的视图中:

NavigationLink(destination: EmptyView()) {
    EmptyView()
}

对于这个可怕的错误,我永远找不到可靠的解决方案。 所以我决定创建一个自定义 NavigationLink,使用Introspect ( https://github.com/siteline/SwiftUI-Introspect )。 这比预期的要好得多,因为所有 swiftui 相关功能继续照常工作。 似乎该错误专门针对 NavigationLink。

private struct NavigationLinkImpl<Destination: View, Label: View>: View {
    let destination: () -> Destination?
    @State var isActive = false
    @ViewBuilder let label: () -> Label

    var body: some View {
        NavigationLinkImpl1(destination: destination, isActive: $isActive, label: label)
    }
}

private struct NavigationLinkImpl1<Destination: View, Label: View>: View {
    let destination: () -> Destination
    @Binding var isActive: Bool
    @ViewBuilder let label: () -> Label
    @State var model = Model()

    var body: some View {
        Button(action: action, label: label)
            .introspectNavigationController(customize: handle)
            .id(isActive)
    }

    func handle(nav: UINavigationController) {
        if isActive {
            if model.destination == nil {
                let dest = UIHostingController<Destination>(rootView: destination())
                nav.pushViewController(dest, animated: true)
                model.destination = dest
            }
        } else {
            if let dest = model.destination {
                if let i = nav.viewControllers.lastIndex(of: dest) {
                    nav.setViewControllers(.init(nav.viewControllers.prefix(i + 1)), animated: true)
                }
                model.destination = nil
            }
        }
        if isActive != model.contains(nav: nav) { // detect pop
            isActive = model.contains(nav: nav)
        }
    }

    final class Model {
        var destination: UIHostingController<Destination>?
        func contains(nav: UINavigationController) -> Bool { destination.map { nav.viewControllers.contains($0) } ?? false }
    }

    func action() { isActive = true }
}

extension NavigationLink {
    init<Destination: View, Label: View>(destination: @autoclosure @escaping () -> Destination, @ViewBuilder label: @escaping () -> Label) {
        self.init(body: NavigationLinkImpl(destination: destination, label: label))
    }

    init<Destination: View, Label: View>(destination: @autoclosure @escaping () -> Destination, isActive: Binding<Bool>, @ViewBuilder label: @escaping () -> Label) {
        self.init(body: NavigationLinkImpl1(destination: destination, isActive: isActive, label: label))
    }

    init<Destination: View>(_ text: String, destination: @autoclosure @escaping () -> Destination, isActive: Binding<Bool>) {
        self.init(destination: destination(), isActive: isActive) { Text(text) }
    }

    init<Destination: View>(_ text: String, destination: @autoclosure @escaping () -> Destination) {
        self.init(destination: destination()) { Text(text) }
    }
}

把它放在一个文件中,你现有的 NavigationLinks 就可以正常工作了。 在 ios 14 和 15 中测试

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM