[SwiftUI]NavigationPathで画面遷移を管理する
NavigationPathとは
NavigationPathとはNavigationStackでの画面遷移をパスとして管理できる構造体です。
NavigationPathを活用すると、階層構造の状態を自由に操作できます。
「UIはSwiftUIだけど画面遷移はUIKit」から脱却できるかもしれません。
完成図
この記事で作成するサンプルアプリの完成図です。
これらの階層構造をコードで管理できるようにします。
階層1 | 階層2 | 階層3 | 階層3 |
---|---|---|---|
階層1. 今日の予定を一覧で確認できる。予定をタップすると詳細画面に遷移する。
階層2. 予定の詳細を確認できる。マップを開く、Webサイトを開くをタップすると各種画面に遷移する。
階層3. Mapで目的地を確認できる。
階層3. Webサイトを確認できる。
NavigationPathの基本的な使い方
struct ContentView: View {
@State private var path = NavigationPath()
@State private var events: [Event] = [Event.meeting, Event.lunch]
var body: some View {
NavigationStack(path: $path) {
List {
ForEach(events) { event in
NavigationLink(event.title, value: event)
}
}
.navigationTitle("今日の予定")
.navigationDestination(for: Event.self) { event in
EventDetailView(event: event)
}
}
}
}
ポイントは3点です。
- NavigationStackにNavigationPathを渡す
- navigationDestinationで、Viewとデータタイプを紐ずける
- NavigationLinkやNavigationPathで画面遷移をする
次の章では上記のコードをベースに紹介、機能の追加をします。
1. NavigationStackにNavigationPathを渡す
NavigationStackに画面遷移のパスを保持するための配列を追加します。
アプリ内で、遷移のフラグとなるデータタイプが単一(navigationDestinationを1つ
しか定義しない)場合は、その型の配列を使用することもできます。
// 複数のデータタイプを扱う場合
@State private var path = NavigationPath()
// Eventのみを扱う場合
@State private var path: [Event] = []
---
NavigationStack(path: $path) {
}
今回は、複数のデータタイプを扱いたいため、NavigationPathを使用します。
NavigationPathは複数の型を1つの配列のように扱える、パスの管理に最適化された構造体です。
2. navigationDestinationで、Viewとデータタイプを紐ずける
EventDetailViewにマップ、Web画面に遷移するための処理を追加します。
navigationDestinationの引数for
には、画面遷移のフラグとして受け取りたいデータタイプを指定します。
enum EventDetailDestination {
case map
case web
}
struct EventDetailView: View {
let event: Event
var body: some View {
List {
Text(event.date, style: .date)
}
.navigationTitle(event.title)
+ .navigationDestination(for: EventDetailDestination.self) { destination in
+ switch destination {
+ case .map:
+ MapView(location: event.location)
+ case .web:
+ WebView(url: event.url)
+ }
+ }
}
}
今回の例では遷移先をEventDetailDestinationというenumで定義しています。
受け取ったEventDetailDestinationの値によって遷移するViewを分岐させています。
navigationDestinationはNavigationStackの中の遅延ロードされないViewであれば、どこにでも追加できます。
画面遷移を実行させる処理の近くに追加すると管理しやすいです。
3. NavigationLinkやNavigationPathで画面遷移をする
NavigationLink
マップ、Web画面に遷移するボタンを追加します。
NavigationLinkの引数value
にはnavigationDestinationが期待するデータタイプの値を記載します。
struct EventDetailView: View {
let event: Event
var body: some View {
List {
Text(event.date, style: .date)
+ Section("Navigation") {
+ NavigationLink("マップを開く", value: EventDetailDestination.map)
+ NavigationLink("Webサイトを開く", value: EventDetailDestination.web)
+ }
}
.navigationTitle(event.title)
.navigationDestination(for: EventDetailDestination.self) {
destination in
switch destination {
case .map:
MapView(location: event.location)
case .web:
WebView(url: event.url)
}
}
}
}
今までのNavigationLinkと同じような使い方ができるようになりました。
NavigationPath
予定一覧の画面に
- ミーティングの場所をマップで開く
- ランチのURLをWebサイトで開く
のような、複数の画面階層に一括で遷移するためのボタンを追加します。
Universal Linksで特定の画面に遷移させたい場合にも有用です。
struct ContentView: View {
@State private var path = NavigationPath()
@State private var events: [Event] = [Event.meeting, Event.lunch]
var body: some View {
NavigationStack(path: $path) {
List {
ForEach(events) { event in
NavigationLink(event.title, value: event)
}
+ Section("Debug") {
+ Button("ミーティング / マップ") {
+ path = NavigationPath([Event.meeting])
+ path.append(EventDetailDestination.map)
+ }
+
+ Button("ランチ / Webサイト") {
+ path = NavigationPath([Event.lunch])
+ path.append(EventDetailDestination.web)
+ }
+ }
}
.navigationTitle("今日の予定")
.navigationDestination(for: Event.self) { event in
EventDetailView(event: event)
}
}
}
}
起動後 | ミーティング / マップをタップ |
---|---|
ミーティング / マップ
をタップすることで、一気にマップ画面に遷移できます。
マップ画面の左上を見るとわかる通り、予定詳細画面の上にマップ画面が載っている状態です。
NavigationPathで画面を遷移する場合は、下記のように配列で上書きしたり、
path = NavigationPath([Event.meeting])
appendを使用して画面を追加します。
path.append(EventDetailDestination.map)
複数の階層からルート画面に戻りたい場合は、下記のように配列を空にします。
path.removeLast(path.count)
必要な場合は、NavigationPathをEnvironmentやBindingを使用して小Viewでも参照することを検討してください。
まとめ
NavigationPathはSwiftUIで画面遷移を管理できる強力なツールです。
従来の方法では提供できなかったリッチなユーザ体験を簡単に提供できます。
この記事が導入の手助けになれば幸いです。
この記事で作成したサンプルアプリはGitHubで公開しています。
Discussion