如何使用SwiftUI关闭多个视图

7
import SwiftUI

struct ContentView: View {
    @State var showSecond = false
    @State var showThird = false

    var body: some View {
        VStack(spacing: 50) {
            Text("FirstView")
            Button("to SecondView") {
                self.showSecond = true
            }
            .sheet(isPresented: $showSecond) {
                VStack(spacing: 50) {
                    Text("SecondView")
                    Button("to ThirdView") {
                        self.showThird = true
                    }
                    .sheet(isPresented: self.$showThird) {
                        VStack(spacing: 50) {
                            Text("ThirdView")
                            Button("back") {
                                self.showThird = false
                            }
                            Button("back to FirstView") {
                                self.showThird = false
                                self.showSecond = false
                            }
                        }
                    }
                    Button("back") {
                        self.showSecond = false
                    }
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

上述代码从FirstView过渡到SecondView,从SecondView过渡到ThirdView。在SecondView和ThirdView的“返回”按钮正常返回上一个屏幕。
然而,如果您在ThirdView中点击“返回到FirstView”按钮,则会显示SecondView而不返回到FirstView。 在此操作之后,当您点击SecondView的“返回”按钮时,它不会返回到FirstView。
如何直接从ThirdView返回到FirstView?
2020年2月19日添加
我已根据答案添加了解决方案代码。
解决方案1:基于Asperi的计划A。
struct ContentView: View {
    @State var showSecond = false
    @State var showThird = false

    var body: some View {
        VStack(spacing: 50) {
            Text("FirstView")
            Button("to SecondView") {
                self.showSecond = true
            }
            .sheet(isPresented: $showSecond) {
                VStack(spacing: 50) {
                    Text("SecondView")
                    Button("to ThirdView") {
                        self.showThird = true
                    }
                    .sheet(isPresented: self.$showThird) {
                        VStack(spacing: 50) {
                            Text("ThirdView")
                            Button("back") {
                                self.showThird = false
                            }
                            Button("back to FirstView") {
                                DispatchQueue.main.async {
                                    self.showThird = false
                                    DispatchQueue.main.async {
                                        self.showSecond = false
                                    }
                                }
                            }
                        }
                    }
                    Button("back") {
                        self.showSecond = false
                    }
                }
            }
        }
    }
}

解决方案2:基于Asperi的计划B。
struct ContentView: View {
    @State var showSecond = false
    @State var showThird = false
    @State var backToFirst = false

    var body: some View {
        VStack(spacing: 50) {
            Text("FirstView")
            Button("to SecondView") {
                self.showSecond = true
            }
            .sheet(isPresented: $showSecond) {
                VStack(spacing: 50) {
                    Text("SecondView")
                    Button("to ThirdView") {
                        self.showThird = true
                    }
                    .sheet(isPresented: self.$showThird, onDismiss: {
                        if self.backToFirst {
                            self.showSecond = false
                        }
                    }) {
                        VStack(spacing: 50) {
                            Text("ThirdView")
                            Button("back") {
                                self.showThird = false
                                self.backToFirst = false
                            }
                            Button("back to FirstView") {
                                self.showThird = false
                                self.backToFirst = true
                            }
                        }
                    }
                    Button("back") {
                        self.showSecond = false
                    }
                }
            }
        }
    }
}

解决方案3:基于Joseph的建议。
struct ContentView: View {
    @State var showSecond = false
    @State var showThird = false

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                VStack(spacing: 50) {
                    Text("FirstView")
                    Button("to SecondView") {
                        self.showSecond = true
                    }
                }
                .frame(width: geometry.size.width, height: geometry.size.height)
                .background(Rectangle().foregroundColor(.white))
                if self.showSecond {
                    VStack(spacing: 50) {
                        Text("SecondView")
                        Button("to ThirdView") {
                            self.showThird = true
                        }
                        Button("back") {
                            self.showSecond = false
                        }
                    }
                    .frame(width: geometry.size.width, height: geometry.size.height)
                    .background(Rectangle().foregroundColor(.white))
                    if self.showThird {
                        VStack(spacing: 50) {
                            Text("ThirdView")
                            Button("back") {
                                self.showThird = false
                            }
                            Button("back to FirstView") {
                                self.showThird = false
                                self.showSecond = false
                            }
                        }
                        .frame(width: geometry.size.width, height: geometry.size.height)
                        .background(Rectangle().foregroundColor(.white))
                    }
                }
            }
        }
    }
}
4个回答

2

目前直接没有办法(而且我认为也永远不会有——这是两个模态会话)……我发现两种可能值得考虑的方法:

A. 从一个地方按顺序关闭

Button("back to FirstView") {
    DispatchQueue.main.async {
        self.showThird = false
        DispatchQueue.main.async {
            self.showSecond = false
        }
    }
}

B. 从不同位置进行顺序关闭

.sheet(isPresented: self.$showThird, onDismiss: {
    self.showSecond = false // with some additional condition for forced back
})...

...

Button("back to FirstView") {
    self.showThird = false
}

谢谢您的建议!我已经尝试了您的建议。计划A没有效果。然而,当我使用“DispatchQueue.main.asyncAfter(deadline: .now() + 0.01)”代替“DispatchQueue.main.async”,它现在按照建议工作了。 计划B按照建议运作良好。当我使用这个计划时,我需要添加一个新的状态变量来确定是否在“.onDismiss”处返回到FirstView。 无论如何,这两个计划都是逐步过渡到返回到FirstView,而不是直接过渡。 我想等待另一个人的解决方案。 - TakeOne
我再次检查了一下。按照所示的代码,planA可以正常工作。当我之前检查时,在设置showThird之前,我忘记了使用DispatchQueue。我已经根据这个建议在问题区添加了一些代码。 - TakeOne

1
Asperi的解决方案适用于同时关闭两个屏幕,但对于更多屏幕则不适用。在这种情况下,我们需要在两种方法中都添加DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)),像这样:
self.showFourth = false
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
    self.showThird = false
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
        self.showSecond = false
    }
}

.milliseconds(200) 不够用。


1

另一个/咳嗽/“解决方案” - 可能是针对略有不同的情况 - 但无论如何:

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: SecondView()) {
                Text("Show Second View")
                    .font(.largeTitle)
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct SecondView: View {
    @Environment(\.presentationMode) var presentationMode

    @State private var showModal = false

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .foregroundColor(.gray)
                    .frame(width: geometry.size.width, height: geometry.size.height)
                Button(action: {
                    withAnimation {
                        self.showModal.toggle()
                    }
                }) {
                    Text("Show Third View")
                        .font(.largeTitle)
                        .foregroundColor(.white)
                }

                if self.showModal {
                    VStack {
                        Button(action: {
                            self.presentationMode.wrappedValue.dismiss()
                        } ) {
                            Text("Show First View")
                                .font(.largeTitle)
                                .foregroundColor(.white)
                        }
                    }
                        .frame(width: geometry.size.width, height: geometry.size.height)
                        .background(Rectangle().foregroundColor(.orange))
                        .transition(.move(edge: .trailing))
                }
            }
            .frame(width: geometry.size.width, height: geometry.size.height)
        }
    }
}

将多个动画视图进行Z堆叠,根据@State布尔值进行渲染,可能是使合成的展开转场具有多层“深度”的一种方法。这不是非常优雅,但是... 是的。 - Joseph Beuys' Mum
谢谢您的建议!您有关使用zstack叠加视图的建议非常有帮助。如果我们不考虑使用sheet来执行模态转换,这种方法效果非常好。我已经根据这个建议在问题区添加了一些代码。 - TakeOne

0

这不是很好看,但你可以像这样回退到UIKit(n是您想要同时关闭的模态视图数量):

private func dismiss(_ n: Int) {
    let rootViewController = UIApplication.shared.connectedScenes
        .filter { $0.activationState == .foregroundActive }
        .map {$0 as? UIWindowScene }
        .compactMap { $0 }
        .first?.windows
        .filter({ $0.isKeyWindow }).first?.rootViewController
    guard let rootViewController = rootViewController else { return }

    var leafFlound = false
    var viewStack: [UIViewController] = [rootViewController]
    while(!leafFlound) {
        if let presentedViewController = viewStack.last?.presentedViewController {
            viewStack.append(presentedViewController)
        } else {
            leafFlound = true
        }
    }
    let presentingViewController = viewStack[max(0, viewStack.count - n - 1)]
    presentingViewController.dismiss(animated: true)
}

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