SwiftUI NavigatorStack 导航容器

NavigationStack 是一个用状态驱动、类型安全的声明式导航容器,它通过管理视图堆栈和导航路径来实现 SwiftUI 应用中的页面导航(专注于单栏场景)

NavigationStack 需要 iOS 16.0+以上版本支持。

核心要素

NavigationStack (导航容器)     │     ├── 管理 NavigationPath (状态存储)     │     ├── 包含 navigationDestination (路由配置)     │       │     │       └── 判断数据类型 → 映射对应的视图     │     └── 视图层(导航的起点) 

NavigationPath 是导航路径容器,用于管理 NavigationStack 的导航状态和历史记录

navigationDestination 是视图修饰符,用于定义数据类型到目标视图的映射关系,相当于导航系统的路由表

基本用法

1、简单页面跳转

可NavigationStack配合NavigationLink实现(不需要使用NavigationPath记录、管理路径)

struct ContentView: View {     var body: some View {         NavigationStack {             List {                 NavigationLink("前往详情页", value: "详情内容")                 NavigationLink("设置", value: "设置页面")             }             .navigationDestination(for: String.self) { value in                 DetailView(content: value)             }         }     } }  struct DetailView: View {     let content: String     var body: some View {         Text("详情: (content)")             .navigationTitle("详情页")     } } 

2、简单页面跳转:多类型路由映射

很多时候,navigationDestination映射的value类型并非只有一种:

struct Test: View {     var body: some View {         NavigationStack {             List {                 // 使用 value 参数 - 必须遵循 Hashable(swift值类型数据默认遵循Hashable协议)                 NavigationLink("使用 value", value: "字符串值")                 NavigationLink("使用数字", value: 42)             }             .navigationDestination(for: String.self) { value in                 Text("字符串值: (value)")             }             .navigationDestination(for: Int.self) { value in                 Text("整数值: (value)")             }         }     } } 

3、简单页面跳转:多类型路由映射2

也可使用枚举管理多种数据类型:

//注意value类型,需要遵循Hashable enum Route: Hashable {     case product(Int)     case profile(String)     case settings }  struct MultiTypeNavigationView: View {     var body: some View {         NavigationStack {             VStack(spacing: 20) {                 NavigationLink("产品详情", value: Route.product(123))                 NavigationLink("用户资料", value: Route.profile("张三"))                 NavigationLink("设置", value: Route.settings)             }             .navigationDestination(for: Route.self) { route in                 switch route {                 case .product(let id):                     ProductDetailView(productId: id)                 case .profile(let username):                     ProfileView(username: username)                 case .settings:                     SettingsView()                 }             }         }     } } 

4、多层页面跳转

可使用NavigationStack、NavigationPath实现

NavigationPath提供了以下方法用于管理路径:

append() : 跳转到新页面

removeLast(): 返回上一页

removeLast(n): 返回前n页

removeAll(): 返回首页

count: 显示当前导航深度

Codable: 实现状态持久化和深度链接

import SwiftUI  // 定义路由枚举 enum Route: Hashable {     case detail(String)     case settings     case profile(Int) }  struct ContentView: View {     @State private var path = NavigationPath()          var body: some View {         NavigationStack(path: $path) {             List {                 Button("跳转到详情页") {                     path.append(Route.detail("Hello World"))                 }                                  Button("跳转到设置") {                     path.append(Route.settings)                 }                                  Button("跳转到用户资料") {                     path.append(Route.profile(123))                 }                                  Button("多层级跳转") {                     path.append(Route.detail("第一层"))                     path.append(Route.settings)                     path.append(Route.profile(456))                 }             }             .navigationTitle("首页")             .navigationDestination(for: Route.self) { route in                 switch route {                 case .detail(let text):                     DetailView(text: text, path: $path)                 case .settings:                     SettingsView(path: $path)                 case .profile(let userId):                     ProfileView(userId: userId, path: $path)                 }             }         }     } }  struct DetailView: View {     let text: String     @Binding var path: NavigationPath          var body: some View {         VStack {             Text("详情页: (text)")                 .font(.title)                          Button("前往下一层") {                 path.append(Route.detail("从详情页跳转"))             }                          Button("返回首页") {                 path.removeLast(path.count)             }                          Button("返回上一层") {                 path.removeLast()             }         }     } }  struct SettingsView: View {     @Binding var path: NavigationPath          var body: some View {         VStack {             Text("设置页面")                 .font(.title)                          Button("返回") {                 path.removeLast()             }         }     } }  struct ProfileView: View {     let userId: Int     @Binding var path: NavigationPath          var body: some View {         VStack {             Text("用户资料: (userId)")                 .font(.title)                          Button("跳转到详情") {                 path.append(Route.detail("来自用户资料"))             }         }     } } 

5、Hashable 的作用

导航必需:NavigationLink 的 value 参数必须遵循 Hashable

路径管理:NavigationPath 依赖 Hashable 来跟踪导航状态

唯一性判断:用于比较两个实例是否代表相同的导航目标

struct NavigationLinkRequirement: View {     var body: some View {         NavigationStack {             List {                 // ✅ 形式1:使用 value 参数 - 必须遵循 Hashable                 NavigationLink("使用 value", value: "字符串值")                 NavigationLink("使用数字", value: 42)                                  // ❌ 这会导致编译错误,因为 MyData 不遵循 Hashable                 // NavigationLink("无效", value: MyData())                                  // ✅ 形式2:使用 destination 参数 - 不需要 Hashable                 NavigationLink("使用 destination") {                     MyCustomView()                 }                                  // ✅ 传统形式 - 不需要 Hashable                 NavigationLink("传统形式", destination: Text("目标视图"))             }             .navigationDestination(for: String.self) { value in                 Text("字符串值: (value)")             }             .navigationDestination(for: Int.self) { value in                 Text("整数值: (value)")             }         }     } }  // ❌ 不遵循 Hashable 的类型 struct MyData {     let content: String }  struct MyCustomView: View {     var body: some View {         Text("自定义视图")     } } 
5.2、NavigationPath 的 Hashable 要求
struct NavigationPathExample: View {     @State private var path = NavigationPath()          var body: some View {         NavigationStack(path: $path) {             VStack(spacing: 20) {                 Text("NavigationPath 演示")                     .font(.title)                                  Button("添加字符串") {                     // ✅ 字符串遵循 Hashable                     path.append("新页面")                 }                                  Button("添加整数") {                     // ✅ 整数遵循 Hashable                     path.append(100)                 }                                  Button("添加自定义类型") {                     // ✅ 只要遵循 Hashable 就可以                     path.append(MyHashableData(name: "测试", id: 1))                 }                                  // ❌ 这会导致运行时错误                 Button("添加非 Hashable 类型(会崩溃)") {                     // path.append(MyData()) // 取消注释会崩溃                 }                                  Button("查看路径深度: (path.count)") {                     print("当前路径深度: (path.count)")                 }                                  Button("返回") {                     if !path.isEmpty {                         path.removeLast()                     }                 }                                  Button("返回根视图") {                     path = NavigationPath() // 重置路径                 }             }             .navigationDestination(for: String.self) { value in                 Text("字符串页面: (value)")             }             .navigationDestination(for: Int.self) { value in                 Text("整数页面: (value)")             }             .navigationDestination(for: MyHashableData.self) { data in                 Text("自定义数据: (data.name) - (data.id)")             }         }     } }  // ✅ 遵循 Hashable 的自定义类型 struct MyHashableData: Hashable {     let name: String     let id: Int } 

6、导航栏UI自定义

NavigationStack 导航结构图:

NavigationStack (容器层)     │     ├── 🏷️ NavigationBar (导航栏层)     │       │     │       ├── ◀️ [toolbar: .topBarLeading]     │       │     ← 返回按钮 / 菜单按钮     │       │     │       ├── 🏷️ [navigationTitle]     │       │     ← 页面标题 (居中显示)     │       │     │       └── ▶️ [toolbar: .topBarTrailing]     │             ← 编辑按钮 / 更多操作     │     └── 📱 Content (内容层)           ← 你的主要视图内容           ← 可以独立滚动 
NavigationStack {                    // ← 根容器     ContentView()                    // ← 📱 内容层         .navigationTitle("标题")     // ← 🏷️ 中央标题         .toolbar {             ToolbarItem(placement: .topBarLeading) {  // ← ◀️ 左侧                 Button("返回") { ... }             }             ToolbarItem(placement: .topBarTrailing) { // ← ▶️ 右侧                   Button("编辑") { ... }             }         } } 

自定义导航栏外观,参考案例:

struct CustomNavigationView: View {     var body: some View {         NavigationStack {             List {                 NavigationLink("标准页面", value: "standard")                 NavigationLink("自定义标题页面", value: "custom")                 NavigationLink("隐藏导航栏页面", value: "hidden")             }             .navigationDestination(for: String.self) { value in                 switch value {                 case "standard":                     StandardView()                 case "custom":                     CustomTitleView()                 case "hidden":                     HiddenNavBarView()                 default:                     Text("未知页面")                 }             }             .navigationTitle("导航演示")             .toolbar {                 ToolbarItem(placement: .navigationBarTrailing) {                     Button("设置") {                         print("设置按钮点击")                     }                 }             }         }         .tint(.purple) // 统一色调     } }  struct StandardView: View {     var body: some View {         Text("标准页面")             .navigationTitle("标准标题")             .navigationBarTitleDisplayMode(.automatic)     } }  struct CustomTitleView: View {     var body: some View {         VStack {             Text("自定义标题页面")                          // 自定义标题视图             Color.blue                 .frame(height: 200)                 .overlay(                     Text("大标题")                         .font(.largeTitle)                         .foregroundColor(.white)                 )         }         .navigationBarTitleDisplayMode(.inline)         .toolbar {             ToolbarItem(placement: .principal) {                 VStack {                     Text("自定义标题")                         .font(.headline)                     Text("副标题")                         .font(.subheadline)                         .foregroundColor(.secondary)                 }             }         }     } }  struct HiddenNavBarView: View {     var body: some View {         Text("隐藏导航栏页面")             .navigationBarBackButtonHidden(true)             .navigationBarHidden(true)             .toolbar(.hidden, for: .navigationBar)     } } 

发表评论

评论已关闭。

相关文章

当前内容话题