NavigationLink — 什么是它,SwiftUI 中的跳转按钮

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

NavigationLink 是 SwiftUI 中的一个控件,用于在 NavigationStack 或 NavigationView 中切换到另一个屏幕。根据 Apple Developer Documentation, 2024NavigationLink 创建一个按钮,点击后会将目标 View 放入导航堆栈中。在 iOS 16+ 中,建议使用带 value 和 NavigationDestination 的 NavigationLink,而不是直接使用 destination,以避免目标 View 的过早初始化。

要点

  • NavigationLink — SwiftUI 中切换到另一个屏幕的按钮
  • 两种形式 — 带 destination:label: 和带 value:label:
  • Value 形式 在 iOS 16+ 中推荐使用 (NavigationStack)
  • Destination 形式 会导致 View 的过早初始化
  • 自动展开箭头 在 List 列表中

NavigationLink 是一个 View,点击后会触发导航跳转。在 NavigationStack 内部,点击 NavigationLink 会将目标屏幕放入堆栈并显示系统的 “返回” 按钮。NavigationLink 自 iOS 13 起就已存在,是 SwiftUI 中用户导航的主要方式。

NavigationLink 不继承自 UIButton — 它是一个 SwiftUI View,会自动适应上下文。在 List 内部,NavigationLink 会显示一个展开指示器(disclosure indicator)。在列表之外,NavigationLink 的行为类似于普通按钮,但具有导航行为。

根据 SwiftUI Lab (2024),NavigationLink 是 SwiftUI 应用程序中最常用的 View 之一,仅次于 Text、Image 和 VStack。理解初始化形式之间的差异对于性能和可预测的导航行为至关重要。

NavigationLink 是如何在底层工作的

点击时,NavigationLink 将一个值(或 destination)添加到与最近的 NavigationStack 或 NavigationView 关联的导航堆栈中。SwiftUI 使用 EnvironmentValue 通过 View 层次结构传递导航路径。NavigationLink 从 Environment 中读取此路径,并在点击时修改它。

NavigationLink 有两种主要形式:带 destination(直接指定目标 View)和带 value(用于 NavigationDestination 的值)。形式的选择取决于 iOS 版本和导航架构。

形式初始化器iOS 13–15iOS 16+
DestinationNavigationLink(destination:label:)推荐不推荐
ValueNavigationLink(value:label:)不可用推荐
IsActiveNavigationLink(isActive:destination:label:)程序化导航不推荐

Destination 形式 (iOS 13+): NavigationLink(destination: DetailView(), label: { Text(“Open”) }) 。这种形式在渲染 NavigationLink 时立即创建 DetailView,即使用户没有点击链接。这会导致 View 的过早初始化,如果目标 View 在初始化器中执行繁重操作,则可能导致潜在的性能问题。

Value 形式 (iOS 16+): NavigationLink(value: “detail_42”, label: { Text(“Open”) }) 。目标 View 仅在点击链接时创建,当 SwiftUI 找到相应的 .navigationDestination 时。这可以防止过早初始化,并使导航更加可预测。

NavigationLink 与 NavigationStack 在 iOS 16+ 中需要切换到 value 形式。您需要为导航定义数据类型(String、Int、enum Route),并通过 .navigationDestination 注册 destination。NavigationLink 只将值放入堆栈,SwiftUI 在点击时创建目标 View。

swift
struct CatalogView: View {
    let categories: [String]

    var body: some View {
        List(categories, id: \.self) { category in
            NavigationLink(value: category) {
                Text(category)
            }
        }
        .navigationDestination(for: String.self) { category in
            CategoryView(name: category)
        }
    }
}

// 程序化导航:
struct DeepLinkView: View {
    @State private var path: [AppRoute] = []

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: AppRoute.self) { route in
                    switch route {
                    case .detail(let id): DetailView(id: id)
                    case .settings: SettingsView()
                    }
                }
                .toolbar {
                    Button("打开设置") {
                        path.append(AppRoute.settings)
                    }
                }
        }
    }
}

程序化导航: 向 path 添加值(通过 path.append)等同于点击具有相同值的 NavigationLink。这允许从 ViewModel、Coordinator 或响应推送通知时实现导航。

IsActive 形式 (NavigationLink(isActive:destination:label:)) 可用于兼容性,但在 iOS 16+ 中不推荐。请使用带 Binding 到路径数组或 NavigationPath 的 value 形式。

NavigationLink 在 List 中 会在行的右侧自动显示一个展开箭头(chevron),向用户指示点击将导航到另一个屏幕。List 自动管理箭头的显示 — 与列表外部的普通 NavigationLink 不同,后者没有箭头。

从 iOS 16 开始,List 中的 NavigationLink 在 List(data:rowContent:) 内部会自动使用 value 形式。在 List 内部使用 ForEach 时,展开箭头也会自动添加。这种行为无法通过修饰器关闭 — 只有将 NavigationLink 替换为 Button 才能移除箭头。

List 中 destination 形式的问题: 如果您在 List 内部使用 NavigationLink(destination:label:),所有目标 View 会在列表加载时立即创建,无论用户是否点击了链接。对于行数较多的列表,这可能会显著减慢初始加载速度并增加内存消耗。使用 NavigationStack 的 value 形式可以解决这个问题。

根据 WWDC 2022 (Session 10054),Apple 建议新项目使用 NavigationStack 和 NavigationLink 的 value 形式。这对于具有动态数据的 List 尤其重要,因为行数可能很多。

模式 1:自定义外观的 NavigationLink。 NavigationLink 接受任何 View 作为 label,允许为链接创建任意设计。在 List 内部,这特别方便 — 使用 NavigationLink 时会自动获得展开箭头。

swift
NavigationLink(value: ProductRoute.detail(product)) {
    HStack {
        AsyncImage(url: product.imageURL)
            .frame(width: 60, height: 60)
        VStack(alignment: .leading) {
            Text(product.name).font(.headline)
            Text(product.price) .foregroundColor(.secondary)
        }
    }
    .padding(8)
}

模式 2:不带箭头的 NavigationLink(自定义按钮)。 如果您不需要展开箭头,请使用 Button 进行程序化导航:path.append(value)。这对于 NavigationLink 看起来不自然的自定义界面元素很有用。

模式 3:条件导航。 您可以通过使用空的 destination 或不添加特定值的 .navigationDestination 来阻止 NavigationLink。通过 path 进行程序化导航允许在添加值之前检查条件。

根据 Hacking with Swift (2024),大多数 NavigationLink 问题与在旧项目中使用 destination 形式有关。在迁移到 NavigationStack 时,将所有 NavigationLink(destination:label:) 替换为 NavigationLink(value:label:),并在根级别添加 .navigationDestination。

常见问题

SwiftUI 中的 NavigationLink 是什么?

NavigationLink 是 SwiftUI 中用于切换到另一个屏幕的 View。点击时,将目标屏幕放入 NavigationStack 或 NavigationView 的导航堆栈中。支持两种形式:带 destination(目标 View)和带 value(用于路由的值)。

哪种 NavigationLink 形式更好:destination 还是 value?

Value 形式 (iOS 16+) 更优:目标 View 仅在点击时创建,而不是在链接渲染时创建。Destination 形式会立即创建 View,这可能会导致性能问题。对于 iOS 16+ 的项目,请使用 value + NavigationDestination。

为什么 NavigationLink 在 List 中会创建一个箭头?

SwiftUI 会自动向 List 内部的 NavigationLink 添加 disclosure indicator(箭头),指示跳转的可能性。此行为无法禁用。如果不需要箭头,请使用 Button 通过 path.append() 进行程序化导航。

如何通过 NavigationLink 进行程序化跳转?

使用带 Binding 路径的 NavigationStack,并通过 path.append(value) 添加值。这等同于点击具有相同 value 的 NavigationLink。程序化导航允许实现 Deeplink、推送通知和 Coordinator 模式。

NavigationLink 是否会影响性能?

如果目标 View 在初始化器中执行 繁重操作,Destination 形式可能会产生影响 — 所有 destination 在列表渲染时都会创建。使用 NavigationStack 的 Value 形式通过仅在点击时创建 View 来解决这个问题。对于 50 行以上的列表,差异显著。

总结

  • NavigationLink — 用于 SwiftUI 屏幕之间导航跳转的按钮
  • Value 形式 在 iOS 16+ 中与 NavigationStack 一起推荐使用
  • Destination 形式 会过早创建 View — 避免用于大型列表
  • Disclosure indicator — List 中的自动箭头(不可关闭)
  • 程序化导航 通过 path.append() 用于 Deeplink 和 Coordinator
  • NavigationDestination 根据数据类型注册目标屏幕
  • IsActive 形式 — 已过时,在 iOS 16+ 上使用 value 形式

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

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

讨论项目

另请阅读