无法在SwiftUI中使用NavigationView和NavigationLink弹出当前屏幕

5

所以我在使用SwiftUI中的NavigationView和NavigationLinks时遇到了一个问题,即尝试弹出当前屏幕,但在这种情况下无法正常工作。

我有3个屏幕 A -> B -> C

每个屏幕都有一个按钮用于弹出自身。

从B -> A的弹出可以正常工作,但是当你从A -> B -> C导航时,从C -> B的弹出就无法正常工作。

您知道可能是什么问题吗?我将在下面附上代码和视频。 该应用程序应支持iOS 14。

enter image description here

struct ScreenA: View {
    @State private var showB = false
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Go to B") {
                    showB = true
                }
                
                NavigationLink(destination: ScreenB(didTapGoBack: {
                    showB = false
                }), isActive: $showB) {
                    EmptyView()
                }
            }
        }
    }
}

struct ScreenB: View {
    var didTapGoBack: () -> Void
    @State var showC = false

    var body: some View {
        VStack {
            Button("Go to C") {
                showC = true
            }
        
            Button("Back to Screen A") {
                didTapGoBack()
            }
            
            NavigationLink(destination: ScreenC(didTapGoBack: {
                showC = false
            }), isActive: $showC) {
                EmptyView()
            }
        }
    }
}

struct ScreenC: View {
    var didTapGoBack: () -> Void
    
    var body: some View {
        VStack {
            Text("Screen C")
            Button("Back to Screen B") {
                didTapGoBack()
            }
        }
    }
}

@main
struct TestNavApp: App {
    var body: some Scene {
        WindowGroup {
            ScreenA()
        }
    }
}
3个回答

3
有一种更好的方法可以做到这一点,并且它具有工作的优势 :)
您可以使用@environment(.dismiss)属性并调用它来关闭当前视图。这样可以使您的代码更简洁。以下是结果:
import SwiftUI

struct ScreenA: View {
    @State private var showB = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: ScreenB(), isActive: $showB) {
                    Text("Go to B")
                }
            }
        }
    }
}

struct ScreenB: View {
    @Environment(\.dismiss) var dismiss
    @State var showC = false
    
    var body: some View {
        VStack {
            
            NavigationLink(destination: ScreenC(), isActive: $showC) {
                Text("Go to C")
            }
            
            Button("Back to Screen A") {
                dismiss()
            }
        }
    }
}

struct ScreenC: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Text("Screen C")
            Button("Back to Screen B") {
                dismiss()
            }
        }
    }
}

请注意,NavigationLink(destination:isActive:)已被弃用。
这是更新后的代码版本:
struct ScreenA: View {
    @State private var showB = false
    
    var body: some View {
        NavigationStack {
            VStack {
                Button("Go to B") {
                    showB = true
                }
            }
            .navigationDestination(isPresented: $showB ){
                ScreenB()
            }
        }
        
    }
}

struct ScreenB: View {
    @Environment(\.dismiss) var dismiss
    @State var showC = false
    
    var body: some View {
        VStack {
            
            Button("Go to C"){
                showC = true
            }
            
            Button("Back to Screen A") {
                dismiss()
            }
        }
        .navigationDestination(isPresented: $showC ){
            ScreenC()
        }
    }
}

struct ScreenC: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Text("Screen C")
            Button("Back to Screen B") {
                dismiss()
            }
        }
    }
}

[编辑] 为了支持iOS 14,我们需要像你在评论中指出的那样使用@Environment(\.presentationMode) var presentationMode
这是代码的iOS 14版本:
struct ScreenA: View {
    @State private var showB = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: ScreenB(), isActive: $showB) {
                    Text("Go to B")
                }
            }
        }
    }
}

struct ScreenB: View {
    @Environment(\.presentationMode) var presentationMode
    @State var showC = false
   
    var body: some View {
        VStack {
            
            NavigationLink(destination: ScreenC(), isActive: $showC) {
                Text("Go to C")
            }
            
            Button("Back to Screen A") {
                presentationMode.wrappedValue.dismiss()
            }
        }
    }
}

struct ScreenC: View {
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack {
            Text("Screen C")
            Button("Back to Screen B") {
                presentationMode.wrappedValue.dismiss()
            }
        }
    }
}

1
谢谢你的回答。是的,它已经被弃用了,但是应用程序需要支持iOS 14 :(. 我知道使用Environment方法可以解决问题,但我想更多地了解为什么原始解决方案不起作用。我需要一个不需要Environment的解决方案,因为它只适用于iOS 15+。 - Mircea Dragota
1
为了在iOS 14上支持环境解决方案,您需要使用以下代码: @Environment(\.presentationMode) var presentationMode presentationMode.wrappedValue.dismiss() - Mircea Dragota
确实,我正在为iOS 14更正我的答案。 - La pieuvre

1
除了@La pieuvre的回答之外,为了使iOS 14上的处理更加简便(就像对iOS 15+的处理方式一样),我们可以为EnvironmentValues创建一个扩展。
extension EnvironmentValues {
    var dismiss: () -> Void {
        { presentationMode.wrappedValue.dismiss() }
    }
}

然后我们可以以更简洁的方式使用它,就像在iOS 15+中一样。
struct ScreenC: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Text("Screen C")
            Button("Back to Screen B") {
                dismiss()
            }
        }
    }
}

这样我们就可以摆脱一直调用presentationMode.wrappedValue.dismiss()


1
所以解决这个问题的方法是在第一个NavigationLink上添加.isDetailLink(false),也许默认情况下NavigationView会像主细节一样工作,所以可能这就是为什么第一个弹出窗口起作用的原因,但通过添加这个,您告诉NavigationView按照堆栈的方式工作。
现在导航按预期工作 A -> B -> C:
struct ScreenA: View {
    @State private var showB = false
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Go to B") {
                    showB = true
                }
                
                NavigationLink(destination: ScreenB(didTapGoBack: {
                    showB = false
                }), isActive: $showB) {
                    EmptyView()
                }.isDetailLink(false)
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接