NavigationLinkは、NavigationStackまたはNavigationViewで別の画面に遷移するために設計されたSwiftUIの制御要素です。Apple Developer Documentation, 2024によると、NavigationLinkは、押下時にターゲットViewをナビゲーションスタックに配置するボタンを作成します。iOS 16+では、ターゲットViewの早期初期化を避けるために、destinationを直接使用するのではなく、valueとNavigationDestinationを組み合わせたNavigationLinkの使用が推奨されています。
重要なポイント
NavigationLinkは、押下時にナビゲーション遷移を開始するViewです。NavigationStack内でNavigationLinkを押すと、ターゲット画面がスタックに配置され、システムの戻るボタンが表示されます。NavigationLinkはiOS 13から存在し、SwiftUIにおけるユーザーナビゲーションの主要な方法です。
NavigationLinkはUIButtonを継承していません — コンテキストに自動的に適応するSwiftUIのViewです。List内では、NavigationLinkは開示インジケータとともに表示されます。リストの外では、NavigationLinkは通常のボタンとして動作しますが、ナビゲーション動作を備えています。
SwiftUI Lab (2024)によると、NavigationLinkは、Text、Image、VStackに次いで、SwiftUIアプリケーションで最も使用されるViewの1つです。初期化形式の違いを理解することは、パフォーマンスと予測可能なナビゲーション動作にとって重要です。
押下されると、NavigationLinkは最も近いNavigationStackまたはNavigationViewに関連付けられたナビゲーションスタックに値(またはdestination)を追加します。SwiftUIはEnvironmentValueを使用して、View階層全体にナビゲーションパスを渡します。NavigationLinkはEnvironmentからこのパスを読み取り、押下時にそれを変更します。
NavigationLinkには2つの主要な形式があります:destination(ターゲットViewを直接指定)とvalue(NavigationDestinationの値)。形式の選択は、iOSのバージョンとナビゲーションアーキテクチャに依存します。
| 形式 | 初期化子 | iOS 13–15 | iOS 16+ |
|---|---|---|---|
| Destination | NavigationLink(destination:label:) | 推奨 | 非推奨 |
| Value | NavigationLink(value:label:) | 利用不可 | 推奨 |
| IsActive | NavigationLink(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を見つけたときにのみ作成されます。これにより早期初期化が防止され、ナビゲーションがより予測可能になります。
NavigationStackでのNavigationLink iOS 16+ではvalue形式への切り替えが必要です。ナビゲーション用のデータ型(String、Int、enum Route)を定義し、.navigationDestinationを介して遷移先を登録します。NavigationLinkはスタックに値のみを配置し、SwiftUIは押下時にターゲットViewを作成します。
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)
}
}
}
// Programmatic navigation:
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("Open Settings") {
path.append(AppRoute.settings)
}
}
}
}
}
プログラムによるナビゲーション:パスに値を追加する(path.append経由)ことは、同じ値のNavigationLinkを押すことと同等です。これにより、ViewModel、Coordinator、またはプッシュ通知への応答からのナビゲーションを実装できます。
IsActive形式(NavigationLink(isActive:destination:label:))は互換性のために利用可能ですが、iOS 16+では推奨されません。パス配列またはNavigationPathへのBindingとともにvalue形式を使用してください。
List内のNavigationLinkは、行の右側に自動的に開示インジケータ(シェブロン)を表示し、押下すると別の画面に移動することをユーザーに示します。Listは矢印の表示を自動的に管理します — リストの外側にある通常のNavigationLinkには矢印がありません。
iOS 16では、NavigationLinkを伴うListは、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をラベルとして受け入れ、リンクのカスタムデザインを作成できます。List内では、NavigationLinkを使用するときに自動的な開示インジケータが得られるため、特に便利です。
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をブロックできます。パスを介したプログラムによるナビゲーションでは、値を追加する前に条件をチェックできます。
Hacking with Swift (2024)によると、NavigationLinkの問題のほとんどは、古いプロジェクトでのdestination形式の使用に関連しています。NavigationStackに移行する際は、すべてのNavigationLink(destination:label:)をNavigationLink(value:label:)に置き換え、ルートレベルで.navigationDestinationを追加してください。
よくある質問
NavigationLinkは、SwiftUIで別の画面に遷移するためのViewです。押下すると、ターゲット画面をNavigationStackまたはNavigationViewのナビゲーションスタックに配置します。destination(ターゲットView)とvalue(ルーティング値)の2つの形式をサポートしています。
Value形式(iOS 16+)が推奨されます:ターゲットViewはリンクのレンダリング時ではなく、押下時にのみ作成されます。Destination形式はViewを即座に作成するため、パフォーマンスの問題を引き起こす可能性があります。iOS 16+のプロジェクトでは、value + NavigationDestinationを使用してください。
SwiftUIは自動的にList内のNavigationLinkに開示インジケータ(矢印)を追加し、ナビゲーションの可能性を示します。この動作は無効にできません。矢印が不要な場合は、path.append()を使用したプログラムによるナビゲーションでButtonを使用してください。
Bindingパスを持つNavigationStackを使用し、path.append(value)で値を追加します。これは同じvalueを持つNavigationLinkを押すことと同等です。プログラムによるナビゲーションにより、ディープリンク、プッシュ通知、Coordinatorパターンを実装できます。
ターゲットViewがその初期化子で重い処理を実行する場合、destination形式はパフォーマンスに影響を与える可能性があります — すべての遷移先がリストのレンダリング時に作成されます。NavigationStackを使用したvalue形式は、押下時にのみViewを作成することでこの問題を解決します。50行以上のリストでは、その差は顕著です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。