52

编辑:这已在 iOS 13.3 中修复!

最小的可重现示例(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内部放置在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 错误还是预期行为?

编辑:我已经在他们的带有 ID 的反馈助手中向 Apple 提出了一个问题,FB7423964以防 Apple 的任何人关心:)。

编辑:我在反馈助手中的公开票表明有 10 多个类似的报告问题。他们已将分辨率更新为Resolution: Potential fix identified - For a future OS update. 手指交叉,修复很快就会着陆。

4

9 回答 9

30

这对我来说是个痛点!我离开了它,直到我的大部分应用程序完成并且我有足够的空间来处理崩溃。

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

在我看来,我会说这是一个BUG。这是我的理由:

  • 如果你在大约半秒的异步延迟中包装presentationModedismiss调用,你应该会发现程序将不再崩溃。

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.presentationMode.wrappedValue.dismiss()
    } 
    
  • 这向我表明,该错误是 SwiftUI 如何与所有其他 UIKit 代码交互以管理各种视图的一种意外行为。根据您的实际代码,您可能会发现如果视图中有一些较小的复杂性,实际上不会发生崩溃。例如,如果您从一个视图中解散到一个具有列表的视图,并且该列表为空,您将在没有异步延迟的情况下发生崩溃。另一方面,如果您在该列表视图中甚至只有一个条目,强制循环迭代以生成父视图,您将看到不会发生崩溃。

我不太确定我将解雇呼叫延迟包装的解决方案有多强大。我必须对其进行更多测试。如果您对此有任何想法,请告诉我!我很高兴向你学习!

于 2019-10-19T18:11:25.503 回答
17

这也让我沮丧了一段时间。在过去的几个月里,根据 Xcode 版本、模拟器版本和真实设备类型和/或版本,它从工作到失败再到再次工作,似乎是随机的。但是,最近它对我来说一直失败,所以昨天我深入研究了它。我目前正在使用 Xcode 版本 11.2.1 (11B500)。

看起来问题围绕着导航栏和按钮添加到它的方式。因此,我没有对按钮本身使用 NavigationLink(),而是尝试使用标准 Button() 和一个操作,该操作设置一个激活隐藏 NavigationLink 的@State var。这是罗伯特父视图的替代品:

struct Parent: View {
    @State private var showingChildView = false
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello World")
                NavigationLink(destination: Child(),
                               isActive: self.$showingChildView)
                { EmptyView() }
                    .frame(width: 0, height: 0)
                    .disabled(true)
                    .hidden()            
             }
             .navigationBarItems(
                 trailing: Button(action:{ self.showingChildView = true }) { Text("Next") }
             )
        }
    }
}

对我来说,这在所有模拟器和所有真实设备上都非常一致。

这是我的助手视图:

struct HiddenNavigationLink<Destination : View>: View {

    public var destination:  Destination
    public var isActive: Binding<Bool>

    var body: some View {

        NavigationLink(destination: self.destination, isActive: self.isActive)
        { EmptyView() }
            .frame(width: 0, height: 0)
            .disabled(true)
            .hidden()
    }
}

struct ActivateButton<Label> : View where Label : View {

    public var activates: Binding<Bool>
    public var label: Label

    public init(activates: Binding<Bool>, @ViewBuilder label: () -> Label) {
        self.activates = activates
        self.label = label()
    }

    var body: some View {
        Button(action: { self.activates.wrappedValue = true }, label: { self.label } )
    }
}

下面是一个用法示例:

struct ContentView: View {
    @State private var showingAddView: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, World!")
                HiddenNavigationLink(destination: AddView(), isActive: self.$showingAddView)
            }
            .navigationBarItems(trailing:
                HStack {
                    ActivateButton(activates: self.$showingAddView) { Image(uiImage: UIImage(systemName: "plus")!) }
                    EditButton()
            } )
        }
    }
}
于 2019-11-16T17:37:54.027 回答
12

这是一个主要错误,我看不到解决它的正确方法。在 iOS 13/13.1 中运行良好,但 13.2 崩溃。

您实际上可以以更简单的方式复制它(此代码实际上就是您所需要的)。

struct ContentView: View {
    var body: some View {
        NavigationView {
            Text("Hello, World!").navigationBarTitle("To Do App")
                .navigationBarItems(leading: NavigationLink(destination: Text("Hi")) {
                    Text("Nav")
                    }
            )
        }
    }
}

希望 Apple 解决它,因为它肯定会破坏大量 SwiftUI 应用程序(包括我的)。

于 2019-11-01T23:27:07.383 回答
6

作为一种解决方法,根据上面 Chuck H 的回答,我将 NavigationLink 封装为隐藏元素:

struct HiddenNavigationLink<Content: View>: View {
var destination: Content
@Binding var activateLink: Bool

var body: some View {
    NavigationLink(destination: destination, isActive: self.$activateLink) {
        EmptyView()
    }
    .frame(width: 0, height: 0)
    .disabled(true)
    .hidden()
}
}

然后您可以在 NavigationView 中使用它(这很重要)并从导航栏中的 Button 触发它:

VStack {
    HiddenNavigationList(destination: SearchView(), activateLink: self.$searchActivated)
    ...
}
.navigationBarItems(trailing: 
    Button("Search") { self.searchActivated = true }
)

将它包含在“//HACK”注释中,这样当 Apple 修复它时,您可以替换它。

于 2019-11-20T17:22:12.937 回答
3

根据你们提供的信息,特别是@Robert 关于 NavigationView 放置位置的评论,我至少在我的特定场景中找到了解决该问题的方法。

就我而言,我有一个 TabView,它包含在 NavigationView 中,如下所示:

struct ContentViewThatCrashes: View {
@State private var selection = 0

var body: some View {
    NavigationView{
        TabView(selection: $selection){
            NavigationLink(destination: NewView()){
                Text("First View")
                    .font(.title)
            }
            .tabItem {
                VStack {
                    Image("first")
                    Text("First")
                }
            }
            .tag(0)
            NavigationLink(destination: NewView()){
                Text("Second View")
                    .font(.title)
            }
            .tabItem {
                VStack {
                    Image("second")
                    Text("Second")
                }
            }
            .tag(1)
        }
    }
  }
}

此代码崩溃,因为每个人都在 iOS 13.2 中报告并在 iOS 13.1 中工作。经过一番研究,我想出了解决这种情况的方法。

基本上,我在每个选项卡上分别将 NavigationView 移动到每个屏幕,如下所示:

struct ContentViewThatWorks: View {
@State private var selection = 0

var body: some View {
    TabView(selection: $selection){
        NavigationView{
            NavigationLink(destination: NewView()){
                Text("First View")
                    .font(.title)
            }
        }
        .tabItem {
            VStack {
                Image("first")
                Text("First")
            }
        }
        .tag(0)
        NavigationView{
            NavigationLink(destination: NewView()){
                Text("Second View")
                    .font(.title)
            }
        }
        .tabItem {
            VStack {
                Image("second")
                Text("Second")
            }
        }
        .tag(1)
    }
  }
}

不知何故违背了 SwiftUI 简单的前提,但它适用于 iOS 13.2。

于 2019-11-06T19:21:32.373 回答
1

Xcode 11.2.1 斯威夫特 5

知道了!我花了几天时间才弄明白这个...

在我使用 SwiftUI 的情况下,只有当我的列表底部超出屏幕然后我尝试“移动”任何列表项时,我才会崩溃。我最终发现,如果我在 List() 下面有太多“东西”,那么它会在移动中崩溃。例如,在我的 List() 下面有一个 Text()、Spacer()、Button()、Spacer() Button()。如果我注释掉其中任何一个对象,那么我突然无法重新创建崩溃。我不确定限制是什么,但是如果您遇到此崩溃,请尝试删除列表下方的对象以查看是否有帮助。

于 2020-01-16T15:38:38.100 回答
0

虽然我看不到任何崩溃,但您的代码存在一些问题:

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

所以不需要在那里有一个按钮。只要保持原样,您就有一个免费的后退按钮。

并且不要忘记根据HIG,后退按钮标题应该显示它的去向,而不是它是什么!所以尝试为第一页设置一个标题,以显示它弹出的任何后退按钮。

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()
    }
}
于 2019-10-20T11:54:21.423 回答
0

FWIW - 以上建议隐藏 NavigationLink Hack 的解决方案仍然是 iOS 13.3b3 中的最佳解决方法。为了子孙后代,我还提交了 FB7386339,并且与上述其他 FB 类似地关闭:“确定的潜在修复 - 用于未来的操作系统更新”。

手指交叉。

于 2019-12-04T01:28:07.797 回答
0

它在 iOS 13.3 中得到解决。只需更新您的操作系统和 xCode。

于 2019-12-18T15:54:21.033 回答