如何在NavigationSplitView中用navigationDestination实现跨视图跳转?
问题背景
正在开发适配macOS、iOS 16的通用App,采用NavigationSplitView实现双栏布局:
- 侧边栏包含两个绑定
NavigationOption类型的NavigationLink,通过.navigationDestination(for: NavigationOption.self)加载对应的VendorsView和TransactionsView VendorsView展示供应商列表,列表项是绑定Vendor类型的NavigationLink,需要跳转到带指定Vendor的TransactionsView- 当前遇到的问题:
- iOS端无法从
VendorsView跳转到指定Vendor的TransactionsView,提示找不到匹配的navigationDestination(macOS端生效) - 代码嵌套多层
NavigationStack和navigationDestination,逻辑混乱 - macOS端跳转到
TransactionsView后,侧边栏的VendorsView链接仍保持高亮,同步选中绑定时会触发额外导航
- iOS端无法从
原问题代码:
struct MainView: View { var body: some View { NavigationSplitView { VStack { // Sidebar List(NavigationOption.navigationOptions) { option in NavigationLink(value: option) { Label(option.name, systemImage: option.icon) } } .navigationDestination(for: NavigationOption.self) { option in NavigationStack { option.viewForPage() .navigationDestination(for: Vendor.self) { vendor in TransactionsView(vendor: vendor) } .id(option.id) } } .navigationDestination(for: Vendor.self) { vendor in TransactionsView(vendor: vendor) } }.navigationTitle("Sidebar") } detail: { NavigationStack { Text("select something") } }
简洁可行的解决方案
通过统一管理导航目标、移除嵌套导航栈、同步选中状态这几个关键点优化,代码逻辑清晰且兼容双平台:
优化后完整代码
// 示例NavigationOption枚举(按你的实际需求调整) enum NavigationOption: Identifiable, CaseIterable { case vendors, transactions var id: Self { self } var name: String { switch self { case .vendors: return "供应商" case .transactions: return "交易记录" } } var icon: String { switch self { case .vendors: return "building.2" case .transactions: return "list.bullet" } } func viewForPage() -> some View { switch self { case .vendors: return VendorsView() case .transactions: return TransactionsView(vendor: nil) } } } // 示例Vendor模型(按你的实际需求调整) struct Vendor: Identifiable { let id = UUID() let name: String } // 主视图优化实现 struct MainView: View { @State private var selectedOption: NavigationOption? @State private var selectedVendor: Vendor? var body: some View { NavigationSplitView { List(NavigationOption.allCases, selection: $selectedOption) { option in NavigationLink(value: option) { Label(option.name, systemImage: option.icon) } } .navigationTitle("Sidebar") // 统一注册NavigationOption的导航目标 .navigationDestination(for: NavigationOption.self) { option in option.viewForPage() } // 统一注册Vendor的导航目标 .navigationDestination(for: Vendor.self) { vendor in TransactionsView(vendor: vendor) .onAppear { // 同步侧边栏选中状态,解决高亮残留问题 selectedOption = .transactions } } } detail: { Text("请选择一个选项") } } } // VendorsView简化实现 struct VendorsView: View { let vendors: [Vendor] = [ Vendor(name: "供应商A"), Vendor(name: "供应商B") ] var body: some View { List(vendors) { vendor in NavigationLink(value: vendor) { Text(vendor.name) } } .navigationTitle("供应商") } } // TransactionsView实现 struct TransactionsView: View { let vendor: Vendor? var body: some View { VStack { if let vendor { Text("查看 \(vendor.name) 的交易记录") } else { Text("查看全部交易记录") } } .navigationTitle("交易记录") } }
关键优化说明
- 统一导航目标位置:将所有
.navigationDestination注册在NavigationSplitView的侧边栏层级,iOS和macOS都能正确识别导航目标,解决iOS找不到跳转目标的问题 - 移除嵌套NavigationStack:
NavigationSplitView的详情栏本身具备导航上下文,无需在导航目标中嵌套额外的NavigationStack,简化逻辑 - 同步侧边栏高亮状态:通过
selection绑定侧边栏列表的选中项,在跳转到TransactionsView时更新选中状态为.transactions,解决macOS侧边栏高亮残留的问题 - 简化子视图逻辑:
VendorsView直接使用绑定Vendor的NavigationLink即可,无需额外嵌套导航目标,代码更简洁
内容的提问来源于stack exchange,提问作者Johannes Dorn
相关产品推荐
相关产品推荐

