NavigationSplitView内NavigationStack路径变更时被清空问题咨询
我在WindowGroup中编写了如下SidebarSelection视图:
struct SidebarSelection: View { @State var selection: Int? @State var path: [Int] = [] var body: some View { NavigationSplitView { List(1...20, id: \.self, selection: $selection) { number in Text("I like \(number)") } } detail: { NavigationStack(path: $path) { VStack { Image(systemName: "x.squareroot") .imageScale(.large) .foregroundColor(.accentColor) Text("This is the NavigationStack root") } .padding() .navigationDestination(for: Int.self) { number in Text("You chose \(number)") } } } .onChange(of: selection) { newValue in print("You clicked \(newValue ?? 0)") if let newValue { path.append(newValue) print("New path \(path)") } } .onChange(of: path) { newValue in print("Path changed to \(path)") } } }
在macOS上运行后,依次点击:
- „I like 5“
- „I like 6“
- „I like 7“
预期结果
详情视图依次显示:
- „You chose 5“
- „You chose 6“
- „You chose 7“
控制台输出应为:
You clicked 5 New path [5] Path changed to [5] You clicked 6 New path [5, 6] Path changed to [5, 6] You clicked 7 New path [5, 6, 7] Path changed to [5, 6, 7]
实际结果
详情视图显示:
- „You chose 5“
- „This is the NavigationStack root“
- „You chose 7“
控制台实际输出:
New path [5] Path changed to [5] You clicked 6 New path [5, 6] Path changed to [] You clicked 7 New path [7] Path changed to [7]
我认为这是一个Bug,已提交反馈并在Apple论坛提问半年,但问题仍未解决。请问我是否操作有误?是什么清空了path,以及如何在NavigationSplitView中正确使用NavigationStack?
问题分析与解决方案
原因
你遇到的path被清空的情况,并非系统Bug,而是macOS上NavigationSplitView的默认行为:
当通过侧边栏的selection绑定切换选项时,系统会自动重置详情区域的NavigationStack路径,目的是让侧边栏的选择与详情视图的根内容一一对应,这是符合分栏视图的设计逻辑的——侧边栏负责切换不同的详情根内容,而非在同一个导航栈中叠加页面。
你的操作逻辑违背了这个设计:手动向path中追加值,而系统会在你选择新的侧边栏项时,自动重置path为空,导致中间步骤的视图跳回根页面。
修复方案
方案1:匹配分栏视图的设计逻辑(推荐)
将path.append(newValue)改为直接赋值path = [newValue],让每次侧边栏选择都直接切换详情视图的根页面:
.onChange(of: selection) { newValue in print("You clicked \(newValue ?? 0)") if let newValue { path = [newValue] // 替换为直接赋值,而非追加 print("New path \(path)") } }
修改后,每次点击侧边栏项,详情视图会直接显示对应的You chose X页面,不会出现path被清空、视图跳回根页面的问题,完全符合NavigationSplitView的预期行为。
方案2:保留导航栈叠加需求
如果你的实际场景确实需要在详情视图的导航栈中叠加页面(比如从侧边栏选择项后,在详情页内继续导航,同时保留侧边栏切换的栈),可以通过NavigationLink替代selection绑定的方式实现:
struct SidebarSelection: View { @State var path: [Int] = [] @State var selection: Int? // 保留选中状态 var body: some View { NavigationSplitView { List(1...20, id: \.self, selection: $selection) { number in NavigationLink(value: number) { Text("I like \(number)") } .tag(number) } } detail: { NavigationStack(path: $path) { VStack { Image(systemName: "x.squareroot") .imageScale(.large) .foregroundColor(.accentColor) Text("This is the NavigationStack root") } .padding() .navigationDestination(for: Int.self) { number in Text("You chose \(number)") } } } .onChange(of: path) { newValue in print("Path changed to \(path)") } } }
这种方式下,点击侧边栏的NavigationLink会自动将值推入path,macOS不会自动重置栈,同时侧边栏的选中状态也能正常保留。
内容的提问来源于stack exchange,提问作者Dominik

