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 来跟踪导航状态
唯一性判断:用于比较两个实例是否代表相同的导航目标
5.1、NavigationLink 的 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)
}
}
标签: NavigationPath, NavigationStack, iOS, SwiftUI, NavigationLink
+加关注
0
0
« 上一篇: vue3 使用swiper轮播组件
posted @ 2025-10-23 20:14 齐行超 阅读(22) 评论(0) 收藏 举报
登录后才能查看或发表评论,立即 登录 或者 逛逛 博客园首页
【推荐】鸿蒙应用开发者激励计划,开发上架应用,现金激励超亿元!
【推荐】博客园&小马算力达成战略合作,为开发者注入“算力引擎”
【推荐】2025 HarmonyOS 创新赛正式启动,百万大奖等你来挑战!
【推荐】天翼云智惠上云月,爆款云主机2核2G只要18.8元/3个月起
【推荐】帮园友公司招聘,杭州吉合信科技诚聘Java全栈开发工程师
编辑推荐:
- 从一个普通程序员的角度,聊聊当前环境下,是否还适合做编程
- 史诗级警报:ASP.NET Core 被曝 CVSS 9.9 分漏洞
- 一个轻量级C++内存监控及可视化开源库
- EF Core: FromExpression 方法有什么用?
- 深度学习优化器算法巧思速览
HarmonyOS专区:
- 开发上架鸿蒙应用,现金激励超亿元!
- 前端转鸿蒙开发几个比较难受的地方
- 闯入鸿蒙:浪漫、理想与「草台班子」
- 3天赚2万!开发者的梦想也可以掷地有声!
- Flutter 适配 HarmonyOS 5 开发知识地图
公告
1、ai 产品研究:音频、图片、文档、文本等办公相关 ☆☆☆☆☆
2、android app ui设计+改版 ☆☆☆☆
3、h5门户网站+通用页面 ☆☆☆
4、server功能支持 ☆☆
5、公司管理相关 ☆
+加关注
|
|||||||||
| 日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
|---|---|---|---|---|---|---|---|---|---|
| 28 | 29 | 30 | 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 | |||
| 12 | 13 | 14 | 15 | 16 | 17 | 18 | |||
| 19 | 20 | 21 | 22 | 23 | 24 | 25 | |||
| 26 | 27 | 28 | 29 | 30 | 31 | 1 | |||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | |||
搜索
我的标签
- Cordova(5)
- native(3)
- JNI(3)
- 鸿蒙(3)
- unity3d(2)
- skin(2)
- QMUI换肤(2)
- NDK(2)
- harmony(2)
- FISCO BCOS 区块链(2)
- 更多
随笔档案
- 2025年10月(1)
- 2024年10月(1)
- 2024年9月(1)
- 2024年7月(1)
- 2024年6月(2)
- 2024年1月(1)
- 2023年9月(1)
- 2022年2月(1)
- 2022年1月(2)
- 2021年10月(2)
- 2021年4月(3)
- 2020年9月(1)
- 2019年12月(1)
- 2019年11月(3)
- 2019年10月(12)
阅读排行榜
- 1. Unity3d粒子特效:制作火焰效果(37040)
- 2. Android 设备唯一标识(多种实现方案)(19367)
- 3. Android原生PDF功能实现(18308)
- 4. Android 禁止截屏、录屏 — 解决PopupWindow无法禁止录屏问题(11248)
- 5. 安卓JNI精细化讲解,让你彻底了解JNI(二):用法解析(9631)
评论排行榜
- 1. Android原生PDF功能实现(6)
- 2. Unity3d粒子特效:制作火焰效果(5)
- 3. 安卓JNI精细化讲解,让你彻底了解JNI(二):用法解析(4)
- 4. Android 设备唯一标识(多种实现方案)(2)
- 5. Android原生PDF功能实现:PDF阅读、PDF页面跳转、PDF手势伸缩、PDF目录树、PDF预览缩略图(2)
推荐排行榜
- 1. Unity3d粒子特效:制作火焰效果(7)
- 2. SpringBoot 过滤器更改 Request body ,并实现数据解密(4)
- 3. 安卓JNI精细化讲解,让你彻底了解JNI(二):用法解析(4)
- 4. Android原生PDF功能实现(4)
- 5. 鸿蒙手机版JNI实战(JNI开发、SO库生成、SO库使用)(3)
最新评论
- 1. Re:Android原生PDF功能实现:PDF阅读、PDF页面跳转、PDF手势伸缩、PDF目录树、PDF预览缩略图
-
楼主能写一下pdf搜索高亮显示吗
- --安卓开发李先生
- 2. Re:安卓JNI精细化讲解,让你彻底了解JNI(二):用法解析
- 感谢作者带来这么好的文章 我注意到一个地方 动态注册中 jni的函数都有jclass提供 而静态注册中 jni的函数都有jobject提供 这个区别的是固定的吗 还是说可以根据需求来改...
- --BlackC0
- 3. Re:Unity3d粒子特效:制作火焰效果
- 您好,非常感谢您能出这样详细的教程,但我看后有一个问题,就是您发的那个素材是怎么制作的,我自己制作的贴图直接拉到粒子系统里只会出现一个一个方块图,而不是图中的内容,想问问您是怎么制作的。...
- --dragonraje
- 4. Re:Android QMUI实战:沉浸式/适配状态栏
-
不错,低代码量解决了问题。
- --语海
- 5. Re:Unity3d粒子特效:制作火焰效果
-
太详细了!
- --远离颠倒梦想1129
更多推荐



所有评论(0)