SwiftUI中导航栈多视图的批量关闭与重置实现问题
SwiftUI 导航栈与Sheet层级控制问题
我在SwiftUI中通过NavigationLink在列表内堆叠了多个视图,视图层级为:ContentView → MediatorView(sheet内)→ FirstListView → SecondListView → ThirdListView。目前需要实现两个功能:
- 点击ThirdListView的「Done」按钮时,关闭所有视图并退出sheet,回到ContentView;
- 点击ThirdListView的「Reset」按钮时,回到sheet的初始视图MediatorView。
原代码未实现上述需求,相关代码如下:
import SwiftUI import Foundation struct ContentView: View { @State var openMediatorView: Bool = false var body: some View { VStack { Button { self.openMediatorView.toggle() } label: { Text("Click Me").font(.body.bold()) } } .sheet(isPresented: self.$openMediatorView) { MediatorView() } } } struct MediatorView: View { let firstList: [String] = ["One", "Two", "Three", "Four", "Five"] let secondList: [String] = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"] let thirdList: [String] = ["One", "Two", "Three", "Four", "Five", "Six", "Seven"] @State var showFirstRow: Bool = false var body: some View { NavigationStack { Group { if firstList.count > 1 { FirstListView(isHidden: .constant(true), shortList: firstList, longList: secondList, thirdList: thirdList) } else { SecondListView(isHidden: .constant(true), longList: secondList, thirdList: thirdList).navigationBarBackButtonHidden() } } .interactiveDismissDisabled() } } } struct FirstListView: View { init(isHidden: Binding<Bool>, shortList: [String], longList: [String], thirdList: [String]) { self.isHidden = isHidden self.shortList = shortList self.longList = longList self.thirdList = thirdList } var isHidden: Binding<Bool> var shortList: [String] var longList: [String] var thirdList: [String] var body: some View { ScrollViewReader { proxy in VStack { List(shortList, id: \.hashValue) { value in NavigationLink(destination: { SecondListView(isHidden: .constant(true), longList: longList, thirdList: thirdList) }, label: { HStack { Text(value) Spacer() } .contentShape(Rectangle()) }) } .listStyle(.plain) .contentShape(Rectangle()) } } .navigationTitle("First List") .interactiveDismissDisabled() } } struct SecondListView: View { init(isHidden: Binding<Bool>, longList: [String], thirdList: [String]) { self.isHidden = isHidden self.longList = longList self.thirdList = thirdList } var isHidden: Binding<Bool> var longList: [String] var thirdList: [String] var body: some View { ScrollViewReader { proxy in VStack { List(longList, id: \.hashValue) { value in NavigationLink(destination: { ThirdListView(isHidden: .constant(true), thirdList: thirdList) }, label: { HStack { Text(value) Spacer() } .contentShape(Rectangle()) }) } .listStyle(.plain) .contentShape(Rectangle()) } } .navigationTitle("Second List") .interactiveDismissDisabled() } } struct ThirdListView: View { @Environment(\.presentationMode) private var presentationMode init(isHidden: Binding<Bool>, thirdList: [String]) { self.isHidden = isHidden self.thirdList = thirdList } var isHidden: Binding<Bool> var thirdList: [String] var body: some View { ScrollViewReader { proxy in VStack { List(thirdList, id: \.hashValue) { value in HStack { Text(value) Spacer() } .contentShape(Rectangle()) } .listStyle(.plain) .contentShape(Rectangle()) Button { isHidden.wrappedValue = false presentationMode.wrappedValue.dismiss() } label: { Text("Reset to Mediator View").font(.body.bold()) } } } .interactiveDismissDisabled() .navigationTitle("Third List") .toolbar { ToolbarItem(placement: .topBarTrailing) { Button { isHidden.wrappedValue = false presentationMode.wrappedValue.dismiss() } label: { Text("Done").font(.body.bold()) } } } } }
解决方案
要实现这两个需求,我们需要通过NavigationStack路径管理控制sheet内的导航层级,同时通过绑定传递sheet展示状态直接关闭整个sheet。修改后的完整代码如下:
import SwiftUI // 枚举标记导航节点,清晰管理层级 enum SheetNavigationPath: Hashable { case secondList case thirdList } struct ContentView: View { @State var openMediatorView: Bool = false var body: some View { VStack { Button { openMediatorView.toggle() } label: { Text("Click Me").font(.body.bold()) } } // 将sheet展示状态绑定传递给MediatorView .sheet(isPresented: $openMediatorView) { MediatorView(isSheetPresented: $openMediatorView) } } } struct MediatorView: View { let firstList: [String] = ["One", "Two", "Three", "Four", "Five"] let secondList: [String] = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"] let thirdList: [String] = ["One", "Two", "Three", "Four", "Five", "Six", "Seven"] @Binding var isSheetPresented: Bool // 管理NavigationStack的导航路径 @State private var navigationPath = [SheetNavigationPath]() var body: some View { NavigationStack(path: $navigationPath) { Group { if firstList.count > 1 { FirstListView(navigationPath: $navigationPath, shortList: firstList, longList: secondList, thirdList: thirdList) } else { SecondListView(navigationPath: $navigationPath, longList: secondList, thirdList: thirdList) .navigationBarBackButtonHidden() } } .interactiveDismissDisabled() // 映射路径对应的目标视图 .navigationDestination(for: SheetNavigationPath.self) { path in switch path { case .secondList: SecondListView(navigationPath: $navigationPath, longList: secondList, thirdList: thirdList) case .thirdList: ThirdListView(navigationPath: $navigationPath, isSheetPresented: $isSheetPresented, thirdList: thirdList) } } } } } struct FirstListView: View { @Binding var navigationPath: [SheetNavigationPath] let shortList: [String] let longList: [String] let thirdList: [String] var body: some View { List(shortList, id: \.self) { value in Button(action: { navigationPath.append(.secondList) }) { HStack { Text(value) Spacer() } } } .listStyle(.plain) .navigationTitle("First List") .interactiveDismissDisabled() } } struct SecondListView: View { @Binding var navigationPath: [SheetNavigationPath] let longList: [String] let thirdList: [String] var body: some View { List(longList, id: \.self) { value in Button(action: { navigationPath.append(.thirdList) }) { HStack { Text(value) Spacer() } } } .listStyle(.plain) .navigationTitle("Second List") .interactiveDismissDisabled() } } struct ThirdListView: View { @Binding var navigationPath: [SheetNavigationPath] @Binding var isSheetPresented: Bool let thirdList: [String] var body: some View { VStack { List(thirdList, id: \.self) { value in HStack { Text(value) Spacer() } } .listStyle(.plain) // Reset按钮:清空导航路径回到MediatorView Button { navigationPath.removeAll() } label: { Text("Reset to Mediator View").font(.body.bold()) } } .interactiveDismissDisabled() .navigationTitle("Third List") .toolbar { ToolbarItem(placement: .topBarTrailing) { // Done按钮:直接关闭整个sheet Button { isSheetPresented = false } label: { Text("Done").font(.body.bold()) } } } } }
关键改动说明
导航路径管理
- 定义
SheetNavigationPath枚举标记各个导航节点,替代原有的硬编码NavigationLink目的地 - 在MediatorView中用
@State private var navigationPath绑定到NavigationStack,通过修改路径数组控制导航层级 - Reset按钮通过
navigationPath.removeAll()清空路径,直接回到MediatorView初始视图
- 定义
Sheet状态传递
- 将ContentView的
openMediatorView绑定传递给MediatorView,再传递到ThirdListView - Done按钮直接设置
isSheetPresented = false,一键关闭整个sheet回到ContentView
- 将ContentView的
导航方式优化
- 用Button+路径append操作替代原NavigationLink(destination:),更灵活控制导航流程,符合现代SwiftUI导航规范
内容的提问来源于stack exchange,提问作者tech_human
相关产品推荐
相关产品推荐

