找回密码
 立即注册
首页 业界区 业界 SwiftUI NavigatorStack 导航容器

SwiftUI NavigatorStack 导航容器

喝岖 前天 20:55
NavigationStack 是一个用状态驱动、类型安全的声明式导航容器,它通过管理视图堆栈和导航路径来实现 SwiftUI 应用中的页面导航(专注于单栏场景)
NavigationStack 需要 iOS 16.0+以上版本支持。
核心要素
  1. NavigationStack (导航容器)
  2.     │
  3.     ├── 管理 NavigationPath (状态存储)
  4.     │
  5.     ├── 包含 navigationDestination (路由配置)
  6.     │       │
  7.     │       └── 判断数据类型 → 映射对应的视图
  8.     │
  9.     └── 视图层(导航的起点)
复制代码
NavigationPath 是导航路径容器,用于管理 NavigationStack 的导航状态和历史记录
navigationDestination  是视图修饰符,用于定义数据类型到目标视图的映射关系,相当于导航系统的路由表
基本用法

1、简单页面跳转

可NavigationStack配合NavigationLink实现(不需要使用NavigationPath记录、管理路径)
  1. struct ContentView: View {
  2.     var body: some View {
  3.         NavigationStack {
  4.             List {
  5.                 NavigationLink("前往详情页", value: "详情内容")
  6.                 NavigationLink("设置", value: "设置页面")
  7.             }
  8.             .navigationDestination(for: String.self) { value in
  9.                 DetailView(content: value)
  10.             }
  11.         }
  12.     }
  13. }
  14. struct DetailView: View {
  15.     let content: String
  16.     var body: some View {
  17.         Text("详情: \(content)")
  18.             .navigationTitle("详情页")
  19.     }
  20. }
复制代码
2、简单页面跳转:多类型路由映射

很多时候,navigationDestination映射的value类型并非只有一种:
  1. struct Test: View {
  2.     var body: some View {
  3.         NavigationStack {
  4.             List {
  5.                 // 使用 value 参数 - 必须遵循 Hashable(swift值类型数据默认遵循Hashable协议)
  6.                 NavigationLink("使用 value", value: "字符串值")
  7.                 NavigationLink("使用数字", value: 42)
  8.             }
  9.             .navigationDestination(for: String.self) { value in
  10.                 Text("字符串值: \(value)")
  11.             }
  12.             .navigationDestination(for: Int.self) { value in
  13.                 Text("整数值: \(value)")
  14.             }
  15.         }
  16.     }
  17. }
复制代码
3、简单页面跳转:多类型路由映射2

也可使用枚举管理多种数据类型:
  1. //注意value类型,需要遵循Hashable
  2. enum Route: Hashable {
  3.     case product(Int)
  4.     case profile(String)
  5.     case settings
  6. }
  7. struct MultiTypeNavigationView: View {
  8.     var body: some View {
  9.         NavigationStack {
  10.             VStack(spacing: 20) {
  11.                 NavigationLink("产品详情", value: Route.product(123))
  12.                 NavigationLink("用户资料", value: Route.profile("张三"))
  13.                 NavigationLink("设置", value: Route.settings)
  14.             }
  15.             .navigationDestination(for: Route.self) { route in
  16.                 switch route {
  17.                 case .product(let id):
  18.                     ProductDetailView(productId: id)
  19.                 case .profile(let username):
  20.                     ProfileView(username: username)
  21.                 case .settings:
  22.                     SettingsView()
  23.                 }
  24.             }
  25.         }
  26.     }
  27. }
复制代码
4、多层页面跳转

可使用NavigationStack、NavigationPath实现
NavigationPath提供了以下方法用于管理路径:
append() : 跳转到新页面
removeLast(): 返回上一页
removeLast(n): 返回前n页
removeAll(): 返回首页
count: 显示当前导航深度
Codable: 实现状态持久化和深度链接
  1. import SwiftUI
  2. // 定义路由枚举
  3. enum Route: Hashable {
  4.     case detail(String)
  5.     case settings
  6.     case profile(Int)
  7. }
  8. struct ContentView: View {
  9.     @State private var path = NavigationPath()
  10.    
  11.     var body: some View {
  12.         NavigationStack(path: $path) {
  13.             List {
  14.                 Button("跳转到详情页") {
  15.                     path.append(Route.detail("Hello World"))
  16.                 }
  17.                
  18.                 Button("跳转到设置") {
  19.                     path.append(Route.settings)
  20.                 }
  21.                
  22.                 Button("跳转到用户资料") {
  23.                     path.append(Route.profile(123))
  24.                 }
  25.                
  26.                 Button("多层级跳转") {
  27.                     path.append(Route.detail("第一层"))
  28.                     path.append(Route.settings)
  29.                     path.append(Route.profile(456))
  30.                 }
  31.             }
  32.             .navigationTitle("首页")
  33.             .navigationDestination(for: Route.self) { route in
  34.                 switch route {
  35.                 case .detail(let text):
  36.                     DetailView(text: text, path: $path)
  37.                 case .settings:
  38.                     SettingsView(path: $path)
  39.                 case .profile(let userId):
  40.                     ProfileView(userId: userId, path: $path)
  41.                 }
  42.             }
  43.         }
  44.     }
  45. }
  46. struct DetailView: View {
  47.     let text: String
  48.     @Binding var path: NavigationPath
  49.    
  50.     var body: some View {
  51.         VStack {
  52.             Text("详情页: \(text)")
  53.                 .font(.title)
  54.             
  55.             Button("前往下一层") {
  56.                 path.append(Route.detail("从详情页跳转"))
  57.             }
  58.             
  59.             Button("返回首页") {
  60.                 path.removeLast(path.count)
  61.             }
  62.             
  63.             Button("返回上一层") {
  64.                 path.removeLast()
  65.             }
  66.         }
  67.     }
  68. }
  69. struct SettingsView: View {
  70.     @Binding var path: NavigationPath
  71.    
  72.     var body: some View {
  73.         VStack {
  74.             Text("设置页面")
  75.                 .font(.title)
  76.             
  77.             Button("返回") {
  78.                 path.removeLast()
  79.             }
  80.         }
  81.     }
  82. }
  83. struct ProfileView: View {
  84.     let userId: Int
  85.     @Binding var path: NavigationPath
  86.    
  87.     var body: some View {
  88.         VStack {
  89.             Text("用户资料: \(userId)")
  90.                 .font(.title)
  91.             
  92.             Button("跳转到详情") {
  93.                 path.append(Route.detail("来自用户资料"))
  94.             }
  95.         }
  96.     }
  97. }
复制代码
5、Hashable 的作用

导航必需:NavigationLink 的 value 参数必须遵循 Hashable
路径管理:NavigationPath 依赖 Hashable 来跟踪导航状态
唯一性判断:用于比较两个实例是否代表相同的导航目标
5.1、NavigationLink 的 Hashable 要求
  1. struct NavigationLinkRequirement: View {
  2.     var body: some View {
  3.         NavigationStack {
  4.             List {
  5.                 // ✅ 形式1:使用 value 参数 - 必须遵循 Hashable
  6.                 NavigationLink("使用 value", value: "字符串值")
  7.                 NavigationLink("使用数字", value: 42)
  8.                
  9.                 // ❌ 这会导致编译错误,因为 MyData 不遵循 Hashable
  10.                 // NavigationLink("无效", value: MyData())
  11.                
  12.                 // ✅ 形式2:使用 destination 参数 - 不需要 Hashable
  13.                 NavigationLink("使用 destination") {
  14.                     MyCustomView()
  15.                 }
  16.                
  17.                 // ✅ 传统形式 - 不需要 Hashable
  18.                 NavigationLink("传统形式", destination: Text("目标视图"))
  19.             }
  20.             .navigationDestination(for: String.self) { value in
  21.                 Text("字符串值: \(value)")
  22.             }
  23.             .navigationDestination(for: Int.self) { value in
  24.                 Text("整数值: \(value)")
  25.             }
  26.         }
  27.     }
  28. }
  29. // ❌ 不遵循 Hashable 的类型
  30. struct MyData {
  31.     let content: String
  32. }
  33. struct MyCustomView: View {
  34.     var body: some View {
  35.         Text("自定义视图")
  36.     }
  37. }
复制代码
5.2、NavigationPath 的 Hashable 要求
  1. struct NavigationPathExample: View {
  2.     @State private var path = NavigationPath()
  3.    
  4.     var body: some View {
  5.         NavigationStack(path: $path) {
  6.             VStack(spacing: 20) {
  7.                 Text("NavigationPath 演示")
  8.                     .font(.title)
  9.                
  10.                 Button("添加字符串") {
  11.                     // ✅ 字符串遵循 Hashable
  12.                     path.append("新页面")
  13.                 }
  14.                
  15.                 Button("添加整数") {
  16.                     // ✅ 整数遵循 Hashable
  17.                     path.append(100)
  18.                 }
  19.                
  20.                 Button("添加自定义类型") {
  21.                     // ✅ 只要遵循 Hashable 就可以
  22.                     path.append(MyHashableData(name: "测试", id: 1))
  23.                 }
  24.                
  25.                 // ❌ 这会导致运行时错误
  26.                 Button("添加非 Hashable 类型(会崩溃)") {
  27.                     // path.append(MyData()) // 取消注释会崩溃
  28.                 }
  29.                
  30.                 Button("查看路径深度: \(path.count)") {
  31.                     print("当前路径深度: \(path.count)")
  32.                 }
  33.                
  34.                 Button("返回") {
  35.                     if !path.isEmpty {
  36.                         path.removeLast()
  37.                     }
  38.                 }
  39.                
  40.                 Button("返回根视图") {
  41.                     path = NavigationPath() // 重置路径
  42.                 }
  43.             }
  44.             .navigationDestination(for: String.self) { value in
  45.                 Text("字符串页面: \(value)")
  46.             }
  47.             .navigationDestination(for: Int.self) { value in
  48.                 Text("整数页面: \(value)")
  49.             }
  50.             .navigationDestination(for: MyHashableData.self) { data in
  51.                 Text("自定义数据: \(data.name) - \(data.id)")
  52.             }
  53.         }
  54.     }
  55. }
  56. // ✅ 遵循 Hashable 的自定义类型
  57. struct MyHashableData: Hashable {
  58.     let name: String
  59.     let id: Int
  60. }
复制代码
6、导航栏UI自定义

NavigationStack 导航结构图:
[code]NavigationStack (容器层)    │    ├──
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册