如何在 SwiftUI 中将呈现视图关闭到选项卡视图的根视图?

How to dismiss a presenting view to the root view of tab view in SwiftUI?

我在主页上使用 TabView。假设我有 4 个标签。 在第二个选项卡上,我可以使用 NavigationLink 转到另一个视图,然后使用 NavigationLink 转到另外两个视图。然后在最新的视图上,有一个按钮可以显示一个视图,我使用 .fullScreenCover(因为我想全屏显示)。

在呈现视图中,我在 navigationBarItems 的左侧添加了一个 X 标记以关闭。我使用 @Environment(\.presentationMode) var presentationModepresentationMode.wrappedValue.dismiss() 来解雇。但它只会将呈现视图关闭到前一个视图,而实际上我想将它关闭到我的视图的根目录,即我的 TabView.

的第二个选项卡

有办法吗?因为我查阅了一些文章,但没有相关内容,尤其是在 TabView 上下文中。

我也有一个问题:

  1. 使用.fullScreenCover是否正确?或者是否有另一种可能的解决方案,例如呈现全屏样式的模式(如果有任何原因我也不确定)。

如有任何建议,我们将不胜感激,提前致谢。

您有 2 个选择:

  1. 使用 .fullScreenCover 你将有一个绑定,导致它被呈现,你可以将这个绑定传递给内容,当用户点击 x 设置为假

  2. 您可以使用 @Environment(\.presentationMode) var presentationMode 然后在您的按钮主体中调用 presentationMode.wrappedValue.dismiss()

编辑:

如果您想完全放松,您应该使 TabView 成为基于绑定的。我喜欢使用 SceneStorage 看看这个 post 然后你可以在你的应用程序中的任何地方访问这个 SceneStorage 值来响应它,也可以更新和更改导航(这个还可以为您提供适当的状态恢复!)

如果您以这种方式制作 TabView:

struct ContentView: View {
    
    @SceneStorage("selectedTab") var selectedTab: Tab = .car
    
    var body: some View {
        TabView(selection: $selectedTab) {
            CarTrips()
                .tabItem {
                    Image(systemName: "car")
                    Text("Car Trips")
                }.tag(Tab.car)
            TramTrips()
                .tabItem {
                    Image(systemName: "tram.fill")
                    Text("Tram Trips")
                }.tag(Tab.tram)
            AirplaneTrips()
                .tabItem {
                    Image(systemName: "airplane")
                    Text("Airplane Trips")
                }.tag(Tab.airplaine)
        }
    }
    

}

enum Tab: String {
    case car
    case tram
    case airplaine
}

然后在您的应用程序中您想要更改导航的地方创建一个按钮视图。

struct ViewCarButton: View {
    @SceneStorage("selectedTab") var selectedTab: Tab = .car

    var body: some View {
        Button("A Button") {
            selectedTab = .car
        }
    }

}

这将强制选择的标签成为 car 标签。

如果您不想更改选项卡而是更改导航视图导航到的内容,您可以为此使用相同的概念,NavigationLink 是一个绑定,如果此绑定是使用 @SceneStorage 然后在您的 ViewCarButton 中,您可以对其进行更改以更改导航状态。

presentationMode是一级效果值,即改变它你关闭一个当前显示的屏幕。

因此,要关闭许多显示的屏幕,您必须以编程方式实现它,如下面的演示所示。

可能的方法是使用自定义 EnvironmentKey 将其向下传递到视图层次结构 w/o 每个级别视图的紧密耦合(如绑定)和 inject/call 仅在该级别需要。

Demo 使用 Xcode 12.4 / iOS 14.4

测试

struct ContentView: View {
    var body: some View {
        TabView {
            Text("Tab1")
                .tabItem { Image(systemName: "1.square") }
            Tab2RootView()
                .tabItem { Image(systemName: "2.square") }
        }
    }
}

struct Tab2RootView: View {
    @State var toRoot = false
    var body: some View {
        NavigationView {
            Tab2NoteView(level: 0)
                .id(toRoot)          // << reset to root !!
        }
        .environment(\.rewind, $toRoot)        // << inject here !!
    }
}

struct Tab2NoteView: View {
    @Environment(\.rewind) var rewind
    let level: Int

    @State private var showFullScreen = false
    var body: some View {
        VStack {
            Text(level == 0 ? "ROOT" : "Level \(level)")
            NavigationLink("Go Next", destination: Tab2NoteView(level: level + 1))
            Divider()
            Button("Full Screen") { showFullScreen.toggle() }
                .fullScreenCover(isPresented: $showFullScreen,
                                        onDismiss: { rewind.wrappedValue.toggle() }) {
                    Tab2FullScreenView()
                }
        }
    }
}

struct RewindKey: EnvironmentKey {
    static let defaultValue: Binding<Bool> = .constant(false)
}

extension EnvironmentValues {
    var rewind: Binding<Bool> {
        get { self[RewindKey.self] }
        set { self[RewindKey.self] = newValue }
    }
}

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

    var body: some View {
        Button("Close") { mode.wrappedValue.dismiss() }
    }
}

backup