NavigationStack: 是什么,SwiftUI 中的导航栈

作者: IT Sectr 发布日期: 2026-06-24 阅读时间: 6 分钟

NavigationStack 是 SwiftUI 中的一个现代导航容器,在 iOS 16+ 中引入,取代了 NavigationView。根据 Apple Developer Documentation, 2024NavigationStack 通过类型安全的导航路径 (NavigationPath) 管理屏幕栈,支持深度导航、程序化返回到根屏幕以及在数据变更时保持状态。与 NavigationView 不同,NavigationStack 不需要额外的容器包装,并提供了直接绑定到导航路径的 Binding。

主要

  • NavigationStack — 新的 SwiftUI 导航容器 (iOS 16+)
  • NavigationPath — 用于管理屏幕栈的类型安全路径
  • Deep linking — 通过路径支持程序化导航
  • NavigationDestination — 根据数据类型注册目标屏幕
  • 状态 自动保存和恢复

SwiftUI 中的 NavigationStack 是什么?

NavigationStack 是一个容器 View,实现了基于栈 (LIFO) 的导航。它管理过渡历史,允许您将新屏幕放置到栈上,并通过系统“返回”按钮或程序化地返回。NavigationStack 是 SwiftUI 的一部分,从 iOS 16、iPadOS 16、macOS 13、watchOS 9 和 tvOS 16 开始。

NavigationStack 的主要创新是类型安全的导航路径。不是在创建 NavigationLink 时直接指定 destination,而是将一个值 (value) 放置到路径中,目标 View 的注册通过 .navigationDestination(for:destination:) 修饰器单独完成。这将导航和显示分离,使代码更加模块化和可测试。

根据 WWDC 2022 (Session 10054),NavigationStack 使用了一种新的基于 ObservableObject 和 SwiftUI 生命周期的导航机制。与依赖于底层 UINavigationController 的 NavigationView 不同,NavigationStack 完全在 SwiftUI 中实现,这提高了可预测性和与 SwiftUI 生命周期的兼容性。

NavigationStack 的基本结构

NavigationStack 接受一个根 View 和一个可选的导航路径 (Binding 到 NavigationPath 或 Hashable 值的数组)。所有子屏幕通过 NavigationLink 或程序化地将值添加到路径来放置到栈中。

NavigationView 是 SwiftUI 的主要导航容器,一直使用到 iOS 16。它在底层自动管理 UINavigationController,这导致了一系列问题:数据变更时的行为不可预测、程序化导航困难以及缺乏类型安全性。

特征NavigationStack (iOS 16+)NavigationView (iOS 13–15)
导航类型栈 (LIFO)栈 (LIFO)
导航路径有类型的 (NavigationPath)不支持
Deep linking内置支持需要未经正式的方法
程序化返回通过路径 (pop, popToRoot)dismiss, presentationMode
底层SwiftUI 原生UINavigationController
兼容性iOS 16+iOS 13+

关键优势 NavigationStack — 类型安全导航。您将路径定义为具体类型的数组 (或对于多种类型的栈使用 NavigationPath),并为每个类型注册 destination。这消除了类型不匹配错误,使导航可预测。

NavigationView 在 iOS 17 中已废弃。Apple 建议所有新项目以及在将最低版本更新到 iOS 16 时迁移到 NavigationStack。

NavigationPath 是一个类型,代表 NavigationStack 中的导航路径。它可以存储多种类型的值 (AnyHashable) 或通过 Binding 到数组 [T: Hashable] 与具体类型一起使用。NavigationPath 会自动编码和解码以保持状态。

swift
struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: String.self) { value in
                    DetailView(id: value)
                }
                .navigationDestination(for: Int.self) { value in
                    NumberView(number: value)
                }
        }
    }

    func goToRoot() {
        path.removeLast(path.count)
    }

    func pushDeepLink() {
        path.append("detail_42")
    }
}

多种类型的栈: NavigationPath 可以包含不同类型的值,隻要它们实现 Hashable。例如,第一个屏幕可以接受 String (ID),第二个接受 Int (数字),第三个接受自定义的 enum Route。每种类型的显示都需要单独注册 .navigationDestination。

Codable 支持: 如果路径中的所有值都是 Codable + Hashable,NavigationPath 就实现 Codable。这可以在应用重启或切换到后台时保存和恢复导航状态。

.navigationDestination(for:destination:) — 一个修饰器,为特定数据类型注册目标 View。当 NavigationLink 将该类型的值放置到路径中时,SwiftUI 自动查找对应的 .navigationDestination 并创建屏幕。

swift
enum AppRoute: Hashable {
    case profile(UserID)
    case settings
    case about
}

struct AppNavigation: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: AppRoute.self) { route in
                    switch route {
                    case .profile(let id):
                        ProfileView(userId: id)
                    case .settings:
                        SettingsView()
                    case .about:
                        AboutView()
                    }
                }
        }
    }
}

// Navigate: path.append(AppRoute.profile("user_123"))

重要规则: .navigationDestination 必须应用于位于 NavigationStack 内部的 View,并且在 NavigationLink 将值放置到路径之前。通常它被添加到根 View 或节的容器。如果没有找到值类型的 .navigationDestination,过渡将不会发生。

根据 SwiftUI Engineering (2023),.navigationDestination 可以在不同层级注册。SwiftUI 在尝试导航时会查找最近的 Destination。这可以在应用的不同部分覆盖某一类型的 destination。

模式 1:通过 enum Route 导航。 为应用的所有屏幕定义一个带关联值的 enum。使用一个 .navigationDestination 用于 AppRoute,使用 switch 进行路由。这为应用中所有可能的过渡提供了唯一的真实来源。

swift
struct StoreView: View {
    @State private var path: [ProductRoute] = []

    var body: some View {
        NavigationStack(path: $path) {
            ProductGrid()
                .navigationDestination(for: ProductRoute.self) { route in
                    switch route {
                    case .detail(let product):
                        ProductDetail(product: product)
                    case .reviews(let productId):
                        ReviewsView(productId: productId)
                    }
                }
        }
    }
}

enum ProductRoute: Hashable {
    case detail(Product)
    case reviews(String)
}

模式 2:程序化导航和 deep linking。 NavigationStack 可以程序化地管理栈:添加、删除屏幕以及返回到根。这对于推送通知、深度链接以及重启后恢复导航是必不可少的。

模式 3:使用类型数组代替 NavigationPath。 如果所有屏幕都使用一种类型 (例如 String 或自定义的 enum),请使用 Binding 到 [T]。这提供了比 NavigationPath 更严格的类型化和更好的性能。NavigationPath 适合不同类型屏幕的多种栈。

根据 Point-Free (2024),使用 enum Route 的 NavigationStack 是组织 SwiftUI 应用中导航的首选方法。它使所有可能的过渡更加明确、类型安全和可测试,这对于有数十个屏幕的大型项目尤为重要。

常见问题

SwiftUI 中的 NavigationStack 是什么?

NavigationStack — 一个 SwiftUI 导航容器 (iOS 16+),通过类型安全的路径管理屏幕栈。它取代了 NavigationView,提供了 deep linking、程序化导航和状态保存的支持。

NavigationStack 与 NavigationView 有什么不同?

NavigationStack 使用 类型安全的路径 (NavigationPath) 代替直接将 NavigationLink 与 destination 连接。它支持程序化导航、deep linking 和 Codable 状态保存。它在 SwiftUI 上工作,而不是通过 UINavigationController。

NavigationPath 如何工作?

NavigationPath 是一个表示栈中屏幕顺序的类型。您通过 path.append() 或使用 NavigationLink 带 value 将值添加到路径。每种类型的显示都需要注册 .navigationDestination。NavigationPath 支持 Codable 和自动状态保存。

如何使用 NavigationStack 实现 deep linking?

通过 程序化路径管理:处理 URL 后,调用 path.append() 带上相应的路由值。NavigationStack 会自动显示目标屏幕。返回到根 — path.removeLast(path.count)。

是否需要从 NavigationView 迁移到 NavigationStack?

是的,如果您的最低版本是 iOS 16+。NavigationView 在 iOS 17 中已废弃。迁移提供了 类型安全导航、deep linking 支持以及与 SwiftUI 生命周期的更好匹配。对于 iOS 15 及更低版本的项目,仍使用 NavigationView。

总结

  • NavigationStack — 现代的 SwiftUI 导航容器 (iOS 16+)
  • NavigationPath — 用于管理屏幕栈的类型安全路径
  • NavigationDestination — 根据数据类型注册目标 View
  • NavigationView 在 iOS 17 中已废弃,建议迁移
  • Deep linking 和程序化导航 — 内置支持
  • Enum Route — 组织导航的首选模式
  • 多种类型栈 通过 NavigationPath 使用任何 Hashable 类型

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读