关闭 SwiftUI TabView 中的子视图

Sip*_*zim 1 swift swiftui

我有一个带有两个选项卡的 SwiftUI TabView,其中一个选项卡使用 NavigationLink 呈现一个子视图。我想要实现的是,当我点击父视图(呈现子视图的选项卡)的选项卡项时,它应该关闭子视图并返回到父视图。

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationView {
                VStack {
                    Text("View1")
                    NavigationLink {
                        Text("Child View")
                    } label: {
                        Text("Go to child view")
                    }
                }
            }
            // when child view is presented, I want when I tap on the tab item view the child view to dismiss. How can I achieve this?
            .tabItem {
                Label("view1", systemImage: "1.circle")
            }
            
            Text("View2")
                .tabItem {
                    Label("view2", systemImage: "2.circle")
                }
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

我尝试了各种方法,包括使用 .onChange 和 @Environment(.presentationMode),但我无法使其按预期工作。

当使用 TabView 并呈现子视图时,有没有办法在 SwiftUI 中实现这种行为?任何帮助或指导将不胜感激。

sup*_*cio 5

至少有两种方法可以实现你想要的:

  1. 使用.id()API 重置NavigationLink.
enum TabSelection {
    case first
    case second
}

private final class ContentViewModel: ObservableObject {
    @Published var tabSelection = TabSelection.first
}

struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
    @State private var tab0Id = UUID()

    var body: some View {
        TabView(selection: $viewModel.tabSelection) {
            NavigationView {
                VStack {
                    Text("View1")
                    NavigationLink {
                        Text("Child View")
                    } label: {
                        Text("Go to child view")
                    }
                    .id(tab0Id)
                }
            }
            .tag(TabSelection.first)
            .tabItem {
                Label("view1", systemImage: "1.circle")
            }

            Text("View2")
                .tabItem {
                    Label("view2", systemImage: "2.circle")
                }
                .tag(TabSelection.second)
        }
        .onReceive(viewModel.$tabSelection) { newValue in
            if newValue == .first {
                tab0Id = UUID()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

在此输入图像描述

  1. 将视图模型作为 an 注入EnvironmentObject到子视图中:
enum TabSelection {
    case first
    case second
}

private final class ContentViewModel: ObservableObject {
    @Published var tabSelection = TabSelection.first
}

struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()

    var body: some View {
        TabView(selection: $viewModel.tabSelection) {
            NavigationView {
                VStack {
                    Text("View1")
                    NavigationLink {
                        ChildView()
                    } label: {
                        Text("Go to child view")
                    }
                }
            }
            .tag(TabSelection.first)
            .tabItem {
                Label("view1", systemImage: "1.circle")
            }

            Text("View2")
                .tabItem {
                    Label("view2", systemImage: "2.circle")
                }
                .tag(TabSelection.second)
        }
        .environmentObject(viewModel)
    }
}

struct ChildView: View {
    @Environment(\.dismiss) private var dismiss: DismissAction
    @EnvironmentObject private var rootViewModel: ContentViewModel

    var body: some View {
        Text("Child View")
            .onReceive(rootViewModel.$tabSelection) { newValue in
                if newValue == .first {
                    dismiss()
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

在此输入图像描述

编辑:只是一个旁注:如果您只需要在第一个选项卡上并点击第一个选项卡项时重置导航,您只需更改逻辑,如下所示onReceive

.onReceive(viewModel.$tabSelection) { [oldValue = viewModel.tabSelection] newValue in
    if oldValue == .first && newValue == .first {
        tab0Id = UUID() // or `dismiss()` if you prefer the second way I described here above
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

在此输入图像描述