最小的可复制示例(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错误还是预期的行为?
编辑:我已经在Apple的反馈助理中打开了一个ID为FB7423964
的苹果问题,以防苹果中的任何人都在想着:)。
编辑:我在反馈助手中打开的票证表明有10多个类似的已报告问题。他们已使用Resolution: Potential fix identified - For a future OS update
更新了分辨率。手指交叉,很快就可以修复。
答案 0 :(得分:16)
这对我来说是很痛苦的一点!我把它留了下来,直到我的大多数应用程序完成为止,并且我有足够的空间来处理崩溃问题。
我认为我们都可以同意SwifUI有一些很棒的东西,但是调试可能很困难。
我认为这是一个错误。这是我的基本原理:
如果以大约半秒的异步延迟包装presentationMode dismiss调用,则应该发现程序将不再崩溃。
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.presentationMode.wrappedValue.dismiss()
}
这向我暗示,该错误是SwiftUI如何与所有其他UIKit代码进行接口以管理各种视图的一种意外行为。根据您的实际代码,您可能会发现,如果视图中存在一些较小的复杂性,则崩溃实际上不会发生。例如,如果您从一个视图中退出到一个具有列表的视图,并且该列表为空,则将在没有异步延迟的情况下崩溃。另一方面,如果在该列表视图中甚至只有一个条目,则强制循环迭代以生成父视图,您将看到崩溃不会发生。
我不确定在延迟中包装dismiss调用的解决方案有多强健。我必须测试更多。如果您对此有任何想法,请告诉我!很高兴向您学习!
答案 1 :(得分:14)
这也让我很沮丧。在过去的几个月中,根据Xcode版本,模拟器版本以及实际设备类型和(或)版本的不同,它似乎从随机运行变为无法正常运行。但是,最近它对我来说一直是失败的,所以昨天我深入研究了它。我当前正在使用Xcode版本11.2.1(11B500)。
问题似乎与导航栏和向其添加按钮的方式有关。因此,我没有使用按钮本身的NavigationLink(),而是尝试使用具有设置@State var的动作的标准Button()来激活隐藏的NavigationLink。这是罗伯特父视图的替代品:
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()
} )
}
}
}
答案 2 :(得分: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应用程序(包括我的)的负载。
答案 3 :(得分:4)
作为一种解决方法,基于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(非常重要)中使用它,并通过导航栏中的按钮触发它:
VStack {
HiddenNavigationList(destination: SearchView(), activateLink: self.$searchActivated)
...
}
.navigationBarItems(trailing:
Button("Search") { self.searchActivated = true }
)
在“ // HACK”注释中将其括起来,以便在Apple修复此问题后可以将其替换。
答案 4 :(得分:2)
根据你们提供的信息,特别是@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上运行。
答案 5 :(得分:1)
Xcode 11.2.1 Swift 5
知道了!我花了几天的时间才解决这个问题...
在我使用SwiftUI的情况下,仅当列表底部超出屏幕范围,然后尝试“移动”任何列表项时,我才会崩溃。我最终发现的是,如果List()下方有太多“东西”,则它在移动时会崩溃。例如,在我的List()下面,我有一个Text(),Spacer(),Button(),Spacer()Button()。如果我注释掉其中任何一个对象,那么突然我将无法重新创建崩溃。我不确定有什么限制,但是如果您遇到此崩溃,请尝试删除列表下方的对象以查看是否有帮助。
答案 6 :(得分: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()
}
}
答案 7 :(得分:0)
FWIW-上面建议隐藏隐藏的NavigationLink Hack的解决方案仍然是iOS 13.3b3中的最佳解决方法。为了后代的缘故,我也提交了FB7386339,并且与上述其他FB相似地被关闭:“已确定潜在修复程序-将来用于OS更新。”
手指交叉。
答案 8 :(得分:0)
在iOS 13.3中已解决。只需更新您的操作系统和xCode。