SwiftUI 关闭多个模态视图窗口

17

我在根视图中使用.sheet(isPresented: self.$showModal)来展示模态框。在模态框中,我使用NavigationView带用户完成各种页面(用于用户资料创建器)。

在我的导航堆栈的最后一个页面中,我使用@Environment(\.presentationMode) var presentationMode和一个按钮调用self.presentationMode.wrappedValue.dismiss()来关闭模态框。但是,这只会关闭导航堆栈中的最后一页,并且我最终只回到上一页。我想要做的是关闭整个导航堆栈并回到根视图。

向下滑动以关闭模态框可以产生预期的结果,但我想通过按钮以编程方式实现这一点。

在SwiftUI中是否有可能实现这一点,或者这是在模态框中使用NavigationView时出现的问题?


过去,简单地调用 self.view.window!.rootViewController?.dismiss(animated: false, completion: nil) 将会取消所有在根视图控制器上方的视图控制器。 - fulvio
这个回答解决了你的问题吗?SwiftUI中的Multiple sheet(isPresented:)无法工作 - Curiosity
6个回答

24

方案1 - 自定义EnvironmentKey

一种可能的解决方案是使用一个自定义的EnvironmentKey来注入到每个环境中:

struct PresentationKey: EnvironmentKey {
    static let defaultValue: [Binding<Bool>] = []
}

extension EnvironmentValues {
    var presentations: [Binding<Bool>] {
        get { return self[PresentationKey] }
        set { self[PresentationKey] = newValue }
    }
}

示例:

在此输入图片描述

struct ContentView: View {
    @Environment(\.presentations) private var presentations
    @State private var showSheet = false

    var body: some View {
        Button("Show sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            SheetView()
                .environment(\.presentations, presentations + [$showSheet])
        }
    }
}
struct SheetView: View {
    @Environment(\.presentations) private var presentations
    @State private var showSheet = false

    var body: some View {
        Button("Show another sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            SheetNavigationView()
                .environment(\.presentations, presentations + [$showSheet])
        }
    }
}
struct SheetNavigationView: View {
    var body: some View {
        NavigationView {
            NavigationLink("Link", destination: SheetNavigationDetailView())
        }
    }
}
struct SheetNavigationDetailView: View {
    @Environment(\.presentations) private var presentations

    var body: some View {
        Button("Pop to root") {
            presentations.forEach {
                $0.wrappedValue = false
            }
        }
    }
}

解决方案2 - 解除UIKit根视图控制器


struct SheetNavigationDetailView: View {

    var body: some View {
        Button("Pop to root") {
            UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true)
        }
    }
}

4
你可以将 showModal 作为绑定传递到以下屏幕中,而不是使用 presentationValue 将 showModal 设置为 false。

谢谢,这对我很有帮助。最终我在NavigationStack的每个页面中都创建了一个@Binding var showModal,然后将其传递到每个后续页面,直到我到达最后一个页面,我真正想要切换它的位置。不确定这是否是最有效的方法,但它可以工作。 - lmh
2
你能否分享一个完整的示例?我正在极力尝试这种方法。将 showModal 作为绑定传递是有效的,但我的导航栈中的每个项目都只是在上一个项目下方略微添加,而不是真正替换它。 - ZbadhabitZ

2
@State private var modalOpen: Bool = false

.sheet(isPresented: self.$modalOpen, onDismiss: {
            // code that triggers when modal is closed
        }) {
        Button(action: {
            self.modalOpen.toggle()
        }) {
           Text("Schließen")
        }

0

SwiftUI:关闭所有活动的弹出视图

let rootViewController = UIApplication.shared.connectedScenes
        .filter {$0.activationState == .foregroundActive }
        .map {$0 as? UIWindowScene }
        .compactMap { $0 }
        .first?.windows
        .filter({ $0.isKeyWindow }).first?.rootViewController
    
    rootViewController?.dismiss(animated: true) {
        // TODO: something
    }

-1
在SwiftUI中。
使用SheetKit来带有动画控制地关闭所有的弹出视图。
SheetKit().dismissAllSheets(animation:false)

或者使用NavigationViewKit在NavigationView中弹出到根视图。

-1
这是一个完整的嵌套模态示例,通过关闭按钮在最后一个屏幕(编号#3)上关闭,供可能需要的人使用。
下面是我们发起和触发模态的主屏幕:
struct OnboardingFlowModal: View {
    @State private var showOnboardingFlow: Bool = false
    
    var body: some View {

        VStack(spacing: 50){
            Text("content of main screen".capitalized)
            
            Spacer()
            
            Button {
                showOnboardingFlow.toggle()
            } label: {
                Text("Start Onboarding")
            }
            .buttonStyle(.borderedProminent)
            .buttonBorderShape(.roundedRectangle(radius: 12))
            .controlSize(.large)
            .padding()
            
        }
        .sheet(isPresented: $showOnboardingFlow,
               onDismiss: didDismiss) {
            ScreenOne(showOnboardingFlow: $showOnboardingFlow)
        }
        
    }
    
    func didDismiss(){
        //dismiss function here
        
    }
}

屏幕#1,它启动模态屏幕的导航流程:
struct ScreenOne: View {
    
    @Binding var showOnboardingFlow: Bool
    
    var body: some View{
        NavigationView {
            VStack{
                Text("Welcome to screen #1".capitalized)
            }
            .navigationTitle("Screen #1")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    NavigationLink {
                        ScreenTwo(showOnboardingFlow: $showOnboardingFlow)
                    } label: {
                        Text("Next")
                    }
                    
                }
            }
        }
    }
}

第二个屏幕仅用于说明目的,在导航中有一个指向最后一个模态框的按钮。
struct ScreenTwo: View {
    
    @Binding var showOnboardingFlow: Bool
    
    var body: some View{
        
        VStack{
            Text("Welcome to screen #2".capitalized)
        }
        .navigationTitle("Screen #2")
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                NavigationLink {
                    ScreenThree(showOnboardingFlow: $showOnboardingFlow)
                } label: {
                    Text("Next")
                }
                
            }
        }
    }
}

第三屏,显示关闭按钮并关闭所有嵌套模态框:

struct ScreenThree: View {
    
    @Binding var showOnboardingFlow: Bool
    
    var body: some View{
        VStack{
            Text("Welcome to screen #3".capitalized)
        }
        .navigationTitle("Screen #3")
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    showOnboardingFlow = false
                } label: {
                    Text("Close")
                }
            }
        }
    }
}

根据目前的写法,你的回答不够清晰。请编辑以添加更多细节,帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community

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