NavigationStackはSwiftUIの最新ナビゲーションコンテナで、iOS 16+で導入されNavigationViewを置き換えました。Apple Developer Documentation, 2024によると、NavigationStackは型安全なナビゲーションパス(NavigationPath)を通じて画面のスタックを管理し、ディープナビゲーション、ルート画面へのプログラム的な復帰、データ変更時の状態保持をサポートします。NavigationViewとは異なり、NavigationStackは追加のコンテナでラップする必要がなく、ナビゲーションパスへの直接のBindingを提供します。
重要なポイント
NavigationStackはスタックベースのナビゲーション(LIFO)を実装するコンテナViewです。遷移履歴を管理し、新しい画面をスタックにプッシュしたり、システムの“戻る”ボタンやプログラム的に戻ることができます。NavigationStackはiOS 16、iPadOS 16、macOS 13、watchOS 9、tvOS 16以降のSwiftUIの一部です。
NavigationStackの主な革新は型安全なナビゲーションパスです。NavigationLinkを作成する際に直接遷移先を指定する代わりに、パスに値を入れ、遷移先Viewは.navigationDestination(for:destination:)修飾子を介して別途登録します。これによりナビゲーションとレンダリングが分離され、コードのモジュール性とテスタビリティが向上します。
WWDC 2022(セッション10054)によると、NavigationStackはObservableObjectとSwiftUIライフサイクルに基づいた新しいナビゲーションメカニズムを使用しています。内部でUINavigationControllerに依存していたNavigationViewとは異なり、NavigationStackは完全にSwiftUIで実装されており、SwiftUIライフサイクルとの予測可能性と互換性が向上しています。
NavigationStackはルートViewとオプションのナビゲーションパス(NavigationPathまたはHashable値の配列へのBinding)を受け入れます。すべての子画面はNavigationLinkを介して、またはプログラム的にパスに値を追加することでスタックにプッシュされます。
NavigationViewはiOS 16以前のSwiftUIにおける主要なナビゲーションコンテナでした。内部で自動的に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)として定義し、各型に遷移先を登録します。これにより型の不一致エラーが排除され、ナビゲーションが予測可能になります。
NavigationViewはiOS 17で非推奨になりました。Appleはすべての新規プロジェクトおよびiOS 16への最小バージョンアップデート時にNavigationStackへの移行を推奨しています。
NavigationPathはNavigationStackのナビゲーションパスを表す型です。異種の値(AnyHashable)を格納したり、配列[T: Hashable]へのBindingを介して特定の型で使用できます。NavigationPathは状態保存のために自動的にエンコードおよびデコードされます。
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)、2番目はInt(数値)、3番目はカスタムenum Routeを受け入れます。各型は表示用に個別の.navigationDestinationを登録します。
Codableサポート: NavigationPathは、パス内のすべての値がCodable + Hashableでもある場合にCodableを実装します。これにより、アプリの再起動時やバックグラウンド移行時にナビゲーション状態を保存および復元できます。
.navigationDestination(for:destination:) — 特定のデータ型の遷移先Viewを登録する修飾子です。NavigationLinkがこの型の値をパスに配置すると、SwiftUIは自動的に対応する.navigationDestinationを見つけて画面を作成します。
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はナビゲーション中に最も近い一致する遷移先を検索します。これにより、アプリケーションの異なる部分で同じ型の遷移先を上書きできます。
パターン1: enum Routeによるナビゲーション。 アプリケーションのすべての画面に関連値を持つenumを定義します。AppRouteに1つの.navigationDestinationとルーティング用のswitchを使用します。これにより、アプリケーション内のすべての可能な遷移の単一の情報源が提供されます。
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など)を使用する場合は、[T]へのBindingを使用します。これによりNavigationPathよりも強力な型付けとパフォーマンスが得られます。NavigationPathは異なる画面タイプを持つ異種スタックに適しています。
Point-Free(2024)によると、enum Routeを使用したNavigationStackはSwiftUIアプリケーションでナビゲーションを整理する推奨方法です。すべての可能な遷移を明示的、型安全、テスト可能にし、数十の画面を持つ大規模プロジェクトで特に重要です。
よくある質問
NavigationStack — 型安全なパスを介して画面のスタックを管理するSwiftUIナビゲーションコンテナ(iOS 16+)です。NavigationViewを置き換え、deep linking、プログラム的ナビゲーション、状態保存をサポートします。
NavigationStackはNavigationLinkを直接遷移先にバインドする代わりに型安全なパス(NavigationPath)を使用します。プログラム的ナビゲーション、deep linking、状態保存のためのCodableをサポートします。UINavigationControllerではなくSwiftUIで動作します。
NavigationPathはスタック内の画面シーケンスを表す型です。path.append()またはvalue付きのNavigationLinkを介してパスに値を追加します。各型は表示用に.navigationDestinationを登録します。NavigationPathはCodableと自動状態保存をサポートします。
プログラム的なパス管理を通じて:URLを処理した後、対応するルート値でpath.append()を呼び出します。NavigationStackは自動的に目的の画面を表示します。ルートに戻る — path.removeLast(path.count)。
はい、最小バージョンがiOS 16+の場合。NavigationViewはiOS 17で非推奨になりました。移行により型安全なナビゲーション、deep linkingサポート、SwiftUIライフサイクルとの整合性が向上します。iOS 15以下のプロジェクトでは、当面はNavigationViewを引き続き使用してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。