iOS16から新たに採用されたNavigationStackでは、navigationDestination Modifierとの組み合わせで、データ駆動型の画面遷移が実現可能です。
navigationDestinationはデータ(データ型)と遷移先との結びつけを定義します。
NavigationStackについてはこちらの記事を御覧ください。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.7.1
【iOS】16.1
【macOS】Monterey バージョン 12.6
基本的な使い方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
struct ContentView: View { var body: some View { /// ナビゲーションの定義 NavigationStack { List { /// 画面遷移リンクの定義(遷移先示す値としてColorを指定) NavigationLink("Mint", value: Color.mint) NavigationLink("Pink", value: Color.pink) NavigationLink("Teal", value: Color.teal) } /// 遷移先を示すデータとしてColor型が指定された時の遷移先定義 .navigationDestination(for: Color.self) { color in ColorDetail(color: color) // 色詳細Viewに遷移 } .navigationTitle("Colors") // ナビゲーションタイトルの定義 } } } /// 色詳細View /// 引数で指定された色を表示するView struct ColorDetail: View { var color: Color var body: some View { color // カラー表示 .navigationTitle(color.description) // タイトルに色名を指定 } } |
データ型と遷移先の関連付け
データ型と遷移先の関連付けは、次のイニシャライザで行います。
データ型には"型名.self"を指定します。
1 2 3 |
.navigationDestination(for: データ型, destination:() -> 遷移先View) |
trailing closureを使って、次のような記述が可能です。
closureはデータの値を引数として受けますので、引数によって遷移先を変えたり、遷移先に引数を渡したりもできます。
1 2 3 4 5 6 7 |
.navigationDestination(for: データ型) { 引数 in /// 遷移先Viewを定義 } |
遷移先を示す値はNavigationLinkで指定します。
1 2 3 |
NavigationLink(value: 値, label: () -> リンクラベル) |
trailing closureを使って、次のような記述が可能です。
1 2 3 4 5 6 7 |
NavigationLink(value: 値) { /// 遷移先を説明するラベルViewを定義 } |
ラベルが文字列のみで構成される場合は、次のイニシャライザが使えます。
1 2 3 |
NavigationLink("ラベルテキスト", value: 値) |
Viewの重なりを配列として管理する
NavigationStack配下での画面遷移は、実際には現在のViewの上に遷移先のViewを重ねていく動作によって実現しています。
データ駆動型の画面遷移では、このViewの重なりをデータの配列として扱えるようになります。
以下に手順を示します。
まずは、対象データ型の空配列を定義します。
1 2 3 |
@State private var colors: [Color] = [] |
そして定義した配列のBindingを次のような形でNavigationStackに渡します。
1 2 3 4 5 6 7 |
NavigationStack(path: $colors) { /// ... } |
この配列を使用して現在のViewのスタック状況が把握できます。また、NavigationLinkを使わずとも、配列の操作によってスタックの内容の変更が可能となります。
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
struct ContentView: View { /// ナビゲーションパス(Viewの重なり状況)を管理する配列 @State private var colors: [Color] = [] var body: some View { /// ナビゲーションの定義 NavigationStack(path: $colors) { // ナビゲーションパスとして配列(colors)を指定 List { /// 画面遷移リンクの定義(遷移先を示す値としてColorを指定) NavigationLink("Mint", value: Color.mint) NavigationLink("Pink", value: Color.pink) NavigationLink("Teal", value: Color.teal) /// Bule画面への遷移ボタン Button("Blue") { colors.append(.blue) // ナビゲーションパスにデータ1件追加 } /// 2色まとめて遷移するボタン Button("Orange + Green") { colors += [.orange, .green] // ナビゲーションパスにデータを2件追加 } } /// 遷移先を示すデータとしてColor型が指定された時の遷移先定義 .navigationDestination(for: Color.self) { color in ColorDetail(color: color) // 色詳細Viewに遷移 } .navigationTitle("Colors") // ナビゲーションタイトルの定義 } } } /// 色詳細View /// 引数で指定された色を表示するView struct ColorDetail: View { var color: Color var body: some View { color // カラー表示 .navigationTitle(color.description) // タイトルに色名を指定 } } |
複数種のデータ型を扱う
遷移先に関連付けるデータの型が複数種ある場合は、navigationDestination をデータ型毎に定義します。
以下はColor型と数値型の2種類のデータ型を各々の遷移先に関連付けた例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
struct ContentView: View { var body: some View { /// ナビゲーションの定義 NavigationStack { Form { /// 画面遷移リンクの定義(遷移先を示す値としてColorを指定) NavigationLink("Mint", value: Color.mint) NavigationLink("Pink", value: Color.pink) NavigationLink("Teal", value: Color.teal) /// 画面遷移リンクの定義(遷移先を示す値として数値を指定) List(1..<4) { index in NavigationLink(value: index) { Text("\(index)") } } } /// 遷移先を示すデータとしてColor型が指定された時の遷移先定義 .navigationDestination(for: Color.self) { color in ColorDetail(color: color) // 色詳細Viewに遷移 } /// 遷移先を示すデータとしてInt型が指定された時の遷移先定義 .navigationDestination(for: Int.self) { index in NumberDetail(index: index) // 数値イメージViewに遷移 } .navigationTitle("Colors & Numbers") // ナビゲーションタイトルの定義 } } } /// 色詳細View /// 引数で指定された色を表示するView struct ColorDetail: View { var color: Color // Colorを引数に受け取る var body: some View { color // 指定されたカラー表示 .navigationTitle(color.description) // タイトルに色名を指定 } } /// 数値イメージView /// 引数で指定された数値のイメージを表示するView struct NumberDetail: View { var index: Int // 数値を引数に受け取る var body: some View { VStack { Image(systemName: "\(index).circle") // 引数に対応したシステムイメージ表示 .resizable() // 画像サイズをフレームサイズに合わせる .scaledToFit() // 縦横比を維持しながらフレームに収める .padding() // 余白付加 .navigationTitle("\(index)") // ナビゲーションタイトルの定義 Spacer() // 上寄せ表示する } } } |
複数種データ型に紐づくViewの重なりを配列として管理する
Viewの重なりを配列として管理するには、対象データ型の配列を使いますが、複数種のデータ型を扱う場合は、NavigationPathを使います。
NavigationPathは異なる型の要素を管理できるコレクションです。
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
struct ContentView: View { /// ナビゲーションパス(Viewの重なり状況)を管理するコレクション @State private var path = NavigationPath() var body: some View { /// ナビゲーションの定義 NavigationStack(path: $path) { Form { /// Blue画面への遷移ボタン Button("indigo") { path.append(Color.indigo) // ナビゲーションパスにColor型のデータを追加 } /// ②画面への遷移ボタン Button("1") { path.append(1) // ナビゲーションパスに数値型のデータを追加 } /// Green画面+④画面への遷移ボタン Button("teal + 3") { path.append(Color.teal) // ナビゲーションパスにColor型のデータを追加 path.append(3) // ナビゲーションパスに数値型のデータを追加 } } /// 遷移先を示すデータとしてColor型が指定された時の遷移先定義 .navigationDestination(for: Color.self) { color in ColorDetail(color: color) // 色詳細Viewに遷移 } /// 遷移先を示すデータとしてInt型が指定された時の遷移先定義 .navigationDestination(for: Int.self) { index in NumberDetail(index: index) // 数値イメージViewに遷移 } .navigationTitle("Colors & Numbers") // ナビゲーションタイトルの定義 } } } /// 色詳細View /// 引数で指定された色を表示するView struct ColorDetail: View { var color: Color // Colorを引数に受け取る var body: some View { color // 指定されたカラー表示 .navigationTitle(color.description) // タイトルに色名を指定 } } /// 数値イメージView /// 引数で指定された数値のイメージを表示するView struct NumberDetail: View { var index: Int // 数値を引数に受け取る var body: some View { VStack { Image(systemName: "\(index).circle") // 引数に対応したシステムイメージ表示 .resizable() // 画像サイズをフレームサイズに合わせる .scaledToFit() // 縦横比を維持しながらフレームに収める .padding() // 余白付加 .navigationTitle("\(index)") // ナビゲーションタイトルの定義 Spacer() // 上寄せ表示する } } } |
navigationDestinationの配置ルール
navigationDestination Modifierの配置については、3つのルールがあります。
1.navigationDestination Modifierは、NavigationStack内に配置する必要があります。
2.List、ScrollView、LazyVStackなどの遅延コンテナに、navigationDestination Modifierを配置してはいけません。
3.同じ型のnavigationDestination Modifierを重ねて定義した場合、最初に定義したものが常に採用されます。