实现 SwiftUI 导航模式,包括 NavigationStack、NavigationSplitView、sheet 呈现、标签导航和深度链接。适用于构建推送导航、程序化路由、多列布局、模态 sheet、标签栏、通用链接或自定义 URL 方案处理。
SwiftUI 导航
适用于 iOS 26+ 和 Swift 6.3 的 SwiftUI 应用导航模式。涵盖推送导航、多列布局、sheet 呈现、标签架构和深度链接。除非另有说明,这些模式向后兼容至 iOS 17。
目录
NavigationStack(推送导航)
使用带有类型化 [Route] 绑定的 NavigationStack 进行程序化推送导航。将路由定义为 Hashable 枚举,并使用 .navigationDestination(for:) 映射;这使路径在编译时得到检查。仅当单个栈必须容纳异构路由值类型时,才使用 NavigationPath。
enum Route: Hashable {
case item(id: Item.ID)
}
struct ContentView: View {
@State private var path: [Route] = []
let items: [Item]
var body: some View {
NavigationStack(path: $path) {
List(items) { item in
NavigationLink(value: Route.item(id: item.id)) {
ItemRow(item: item)
}
}
.navigationDestination(for: Route.self) { route in
switch route {
case .item(let id):
DetailView(itemID: id)
}
}
.navigationTitle("Items")
}
}
}
程序化导航:
path.append(.item(id: item.id)) // 推送
path.removeLast() // 弹出
path = [] // 返回根
路由器模式: 对于具有复杂导航的应用,使用一个拥有路径和 sheet 状态的路由器对象。每个标签通过 .environment() 注入自己的路由器实例。使用单个 .navigationDestination(for:) 块或共享的 withAppRouter() 修饰符集中目标映射。
有关完整的路由器示例,包括每个标签的栈、集中目标映射和通用标签路由,请参阅 references/navigationstack.md。
NavigationSplitView(多列)
在 iPad 和 Mac 上使用 NavigationSplitView 实现侧边栏-详情布局。在 iPhone 上回退到栈导航。
struct MasterDetailView: View {
@State private var selectedItem: Item?
var body: some View {
NavigationSplitView {
List(items, selection: $selectedItem) { item in
NavigationLink(value: item) { ItemRow(item: item) }
}
.navigationTitle("Items")
} detail: {
if let item = selectedItem {
ItemDetailView(item: item)
} else {
ContentUnavailableView("Select an Item", systemImage: "sidebar.leading")
}
}
}
}
自定义分割列(手动 HStack)
对于自定义多列布局(例如,独立于选择的专用通知列),使用带有 horizontalSizeClass 检查的手动 HStack 分割:
@MainActor
struct AppView: View {
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
@AppStorage("showSecondaryColumn") private var showSecondaryColumn = true
var body: some View {
HStack(spacing: 0) {
primaryColumn
if shouldShowSecondaryColumn {
Divider().edgesIgnoringSafeArea(.all)
secondaryColumn
}
}
}
private var shouldShowSecondaryColumn: Bool {
horizontalSizeClass == .regular
&& showSecondaryColumn
}
private var primaryColumn: some View {
TabView { /* 标签 */ }
}
private var secondaryColumn: some View {
NotificationsTab()
.environment(\.isSecondaryColumn, true)
.frame(maxWidth: .secondaryColumnWidth)
}
}
当需要完全控制或非标准辅助列时,使用手动 HStack 分割。当需要标准系统布局且最小自定义时,使用 NavigationSplitView。
Sheet 呈现
当状态表示选中的模型时,优先使用 .sheet(item:) 而不是 .sheet(isPresented:)。Sheet 应拥有自己的操作并在内部调用 dismiss()。
@State private var selectedItem: Item?
.sheet(item: $selectedItem) { item in
EditItemSheet(item: item)
}
呈现尺寸(iOS 18+): 使用 .presentationSizing 控制 sheet 尺寸:
.sheet(item: $selectedItem) { item in
EditItemSheet(item: item)
.presentationSizing(.form) // .form, .page, .fitted, .automatic
}
PresentationSizing 值:
.automatic-- 平台默认.page-- 大致纸张大小,用于信息内容.form-- 比 page 稍窄,用于表单样式 UI.fitted-- 由内容的理想大小决定
微调:.fitted(horizontal:vertical:) 约束适配轴;.sticky(horizontal:vertical:) 在指定维度上增长但不缩小。
关闭保护: 在 iOS/iPadOS 上,使用 .interactiveDismissDisabled(hasUnsavedChanges) 并在 sheet 内部提供明确的保存/放弃操作。在 macOS 15+ 上,使用 .dismissalConfirmationDialog("Discard?", shouldPresent: hasUnsavedChanges) 进行窗口关闭确认。所有程序化关闭都通过相同的保存/验证/放弃门控;interactiveDismissDisabled 仅保护交互式关闭。
枚举驱动的 sheet 路由: 定义一个 Identifiable 的 SheetDestination 枚举,将其存储在路由器上,并使用共享视图修饰符映射。这允许任何子视图呈现 sheet 而无需属性传递。有关完整的集中式 sheet 路由模式,请参阅 references/sheets.md。
标签导航
使用带有选择绑定的 Tab API 实现可扩展的标签架构。每个标签应将其内容包装在独立的 NavigationStack 中。
struct MainTabView: View {
@State private var selectedTab: AppTab = .home
var body: some View {
TabView(selection: $selectedTab) {
Tab("Home", systemImage: "house", value: .home) {
NavigationStack { HomeView() }
}
Tab("Search", systemImage: "magnifyingglass", value: .search) {
NavigationStack { SearchView() }
}
Tab("Profile", systemImage: "person", value: .profile) {
NavigationStack { ProfileView() }
}
}
}
}
带副作用的自定义绑定: 通过函数路由选择更改,以拦截应触发操作而非更改选择的特殊标签(例如,撰写)。
iOS 26 标签新增功能
Tab(value:role:)与.search-- 标记专用搜索标签,具有系统默认搜索标题、图标和固定行为.tabViewSearchActivation(_:)-- 控制搜索标签的激活和停用行为.tabBarMinimizeBehavior(_:)--.onScrollDown,.onScrollUp,.never(仅 iPhone).tabViewSidebarHeader/Footer-- 在 iPadOS/macOS 上自定义侧边栏部分.tabViewBottomAccessory { }-- 在标签栏下方附加内容(例如,正在播放栏)TabSection-- 使用.tabPlacement(.sidebarOnly)将标签分组到侧边栏部分
有关完整的 TabView 模式,包括自定义绑定、动态标签和侧边栏自定义,请参阅 references/tabview.md。
深度链接
使用 解析 → 验证 → 提交。将 URL 解析为类型化路由而不改变导航;验证方案/主机/路径、标识符形状、授权和目标存在性;然后原子地更新标签/路径。无效链接必须保持当前导航不变。
通用链接
通用链接允许 iOS 为标准 HTTPS URL 打开您的应用。它们需要:
- 在
/.well-known/apple-app-site-association放置 Apple App Site Association (AASA) 文件 - 关联域授权(
applinks:example.com)
在 SwiftUI 中使用 .onOpenURL 处理通用链接和自定义 URL 方案:
@main
struct MyApp: App {
@State private var router = Router()
var body: some Scene {
WindowGroup {
ContentView()
.environment(router)
.onOpenURL { url in router.handle(url: url) }
}
}
}
自定义 URL 方案
在 Info.plist 的 CFBundleURLTypes 下注册方案。使用 .onOpenURL 处理。对于公开共享的链接,优先使用通用链接而非自定义方案——它们提供 Web 回退和域验证。
Handoff(NSUserActivity)
使用 .userActivity() 宣传活动,并使用 .onContinueUserActivity() 接收 Handoff 或其他用户活动。在 Info.plist 的 NSUserActivityTypes 下声明活动类型。设置 isEligibleForHandoff = true 并提供 webpageURL 作为回退。
有关 AASA 配置、路由器 URL 处理、自定义 URL 方案和 NSUserActivity 延续的完整示例,请参阅 references/deeplinks.md。
常见错误
- 使用已弃用的
NavigationView-- 改用NavigationStack或NavigationSplitView - 在所有标签间共享一个导航路径或路由器 -- 每个标签需要自己的路径
- 当状态表示模型时使用
.sheet(isPresented:)-- 改用.sheet(item:) - 在导航路径中存储视图实例 -- 存储轻量级的
Hashable路由数据 - 将
@Observable路由器对象嵌套在其他@Observable对象中 - 优先使用
Tab(value:)和TabView(selection:)而非旧的.tabItem { }API - 假设
tabBarMinimizeBehavior在 iPad 上有效 -- 它仅适用于 iPhone - 在多个地方处理深度链接 -- 在路由器中集中 URL 解析
- 硬编码 sheet 框架尺寸 -- 改用
.presentationSizing(.form) - 路由器类缺少
@MainActor-- Swift 6 并发安全需要
审查清单
- [ ] 使用
NavigationStack(而非NavigationView) - [ ] 每个标签拥有自己的
NavigationStack和独立路径 - [ ] 路由枚举是
Hashable且具有稳定标识符 - [ ]
.navigationDestination(for:)映射所有路由类型 - [ ] 优先使用
.sheet(item:)而非.sheet(isPresented:) - [ ] Sheet 在内部拥有自己的关闭逻辑
- [ ] 路由器对象是
@MainActor和@Observable - [ ] 深度链接 URL 在导航前经过解析和验证
- [ ] 通用链接已配置 AASA 和关联域
- [ ] 标签选择使用带有绑定的
Tab(value:)
参考资料
- NavigationStack 和路由器模式:references/navigationstack.md
- Sheet 呈现和路由:references/sheets.md
- TabView 模式和 iOS 26 API:references/tabview.md
- 深度链接、通用链接和 Handoff:references/deeplinks.md
- 架构和状态管理:参见
swiftui-patterns技能 - 布局和组件:参见
swiftui-layout-components技能






