为什么在将NavigationLink放在NavigationView的navigationBarItems内后向后导航时,我的SwiftUI应用会崩溃?

问题描述 投票:1回答:2

最小的可复制示例(Xcode 11.2 beta,在Xcode 11.1中有效):

struct Parent: View {
    var body: some View {
        NavigationView {
            Text("Hello World")
                .navigationBarItems(
                    trailing: NavigationLink(destination: Child(), label: { Text("Next") })
                )
        }
    }
}

struct Child: View {
    @Environment(\.presentationMode) var presentation
    var body: some View {
        Text("Hello, World!")
            .navigationBarItems(
                leading: Button(
                    action: {
                        self.presentation.wrappedValue.dismiss()
                    },
                    label: { Text("Back") }
                )
            )
    }
}

struct ContentView: View {
    var body: some View {
        Parent()
    }
}

[问题似乎在于将我的NavigationLink放在嵌套在SwiftUI视图中的navigationBarItems修饰符内,该修饰符的根视图是NavigationView。崩溃报告表明,当我向前导航至Child然后返回至Parent时,我试图弹出一个不存在的视图控制器。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Tried to pop to a view controller that doesn't exist.'
*** First throw call stack:

[如果我将NavigationLink放置在如下所示的视图主体中,则可以正常工作。

struct Parent: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: Child(), label: { Text("Next") })
        }
    }
}

这是SwiftUI错误还是预期的行为?

ios swift crash uinavigationcontroller swiftui
2个回答
1
投票
这对我来说是很痛苦的一点!我把它留了下来,直到我的大多数应用程序完成为止,并且我有足够的空间来处理崩溃问题。

[我想我们都同意SwifUI有一些很棒的东西,但是调试可能很困难。

我认为这是一个错误。这是我的基本原理:

  • 如果您以大约半秒的异步延迟包装presentationMode dismiss调用,您应该发现程序将不再崩溃。///

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.presentationMode.wrappedValue.dismiss() }

  • 这向我暗示,该错误是SwiftUI如何与所有其他UIKit代码进行接口以管理各种视图的一种意外行为。根据您的实际代码,您可能会发现,如果视图中的复杂性较小,则崩溃实际上不会发生。例如,如果您从一个视图中退出到一个具有列表的视图,并且该列表为空,则将在没有异步延迟的情况下崩溃。另一方面,如果在该列表视图中甚至只有一个条目,则强制循环迭代以生成父视图,您将看到崩溃不会发生。
  • 我不确定在延迟中包装dismiss调用的解决方案有多健壮。我必须测试更多。如果您对此有任何想法,请告诉我!我很高兴向您学习!


    0
    投票
    尽管我看不到任何崩溃,但是您的代码有一些问题:

    通过设置前导项目,您实际上杀死了导航过渡的默认行为。 (尝试从前端滑动以查看是否有效)。

    因此无需在此处设置按钮。只需保留它,您就可以拥有一个免费的后退按钮。

    并且根据

    HIG别忘了,后退按钮标题应该显示它的去向,not它是什么!因此,请尝试为首页设置标题,以向其显示弹出的所有后退按钮。

    struct Parent: View { var body: some View { NavigationView { Text("Hello World") .navigationBarItems( trailing: NavigationLink(destination: Child(), label: { Text("Next") }) ) .navigationBarTitle("First Page",displayMode: .inline) } } } struct Child: View { @Environment(\.presentationMode) var presentation var body: some View { Text("Hello, World!") } } struct ContentView: View { var body: some View { Parent() } }
    © www.soinside.com 2019 - 2024. All rights reserved.