💁♂️
【SwiftUI】NavigationStackでナビゲーションパスを保持して手動で制御する
はじめに
SwiftUIにおけるナビゲーション管理の方法について、以下2つの方法を紹介します。
- NavigationLinkに遷移先のViewを紐づけて直接的にナビゲーションを実行する
- NavigationLinkにデータを紐づけて間接的にナビゲーションを実行する
この記事の主張
- 方法1: NavigationLinkに遷移先のViewを紐づけて直接的にナビゲーションを実行
- 方法2: NavigationLinkにデータを紐づけて間接的にナビゲーションを実行する
- 方法2はナビゲーションパスを記録することが容易になる
- 方法2でナビゲーションを手動制御する
本題
方法1: NavigationLinkに遷移先のViewを紐づけて直接的にナビゲーションを実行
まずは、NavigationLinkに遷移先のViewを紐づけて直接的にナビゲーションを実行する方法についてです。
import SwiftUI
struct ParentView1: View {
var body: some View {
NavigationStack {
List {
NavigationLink("ChildView1") {
ChildView(data: "1")
}
NavigationLink("ChildView2") {
ChildView(data: "2")
}
NavigationLink("ChildView3") {
ChildView(data: "3")
}
}
.navigationTitle("ParentView")
}
}
}
方法2: NavigationLinkにデータを紐づけて間接的にナビゲーションを実行する
次に、NavigationLinkにデータを紐づける方法です。
ポイントは以下のとおりです。
- 各NavigationLinkにデータ値
1
2
3
を割り当ててリンクを作成 -
navigationDestination(for:destination:)
ビュー修飾子で遷移先ビューにデータを関連づける
import SwiftUI
struct ParentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink("ChildView1", value: "1")
NavigationLink("ChildView2", value: "2")
NavigationLink("ChildView3", value: "3")
}
.navigationTitle("Parent View")
.navigationDestination(for: String.self) { value in
ChildView(data: value)
}
}
}
}
方法2のメリットは、以下のとおりです。
- データ値をNavigationLinkに割り当てているため、ナビゲーションパスを記録できるようになる
- 結果的にナビゲーションを手動制御できるようになる
次の節では、ナビゲーションパスの記録方法とナビゲーションの手動制御について解説します。
方法2はナビゲーションパスを記録することが容易になる
方法2を前提として、ナビゲーションパスを記録する方法です。
ポイントは以下のとおりです。
- NavigationLinkのvalueを格納する配列
navigationPath
を用意する(Stateプロパティラッパーをつける) -
NavigationStack(path:root:)
でナビゲーションを管理する -
navigationDestination(for:destination:)
で遷移先のビューを指定する
import SwiftUI
struct ParentView: View {
@State private var navigationPath: [String] = []
var body: some View {
NavigationStack(path: $navigationPath) {
List {
NavigationLink("ChildView1", value: "1")
NavigationLink("ChildView2", value: "2")
NavigationLink("ChildView3", value: "3")
}
.navigationTitle("Parent View")
.navigationDestination(for: String.self) { value in
ChildView(data: value)
}
}
}
}
こうすることで、遷移時にNavigationLinkのデータがナビゲーションパスの末尾に記録されます。
[] //親ビュー
["1"] //子ビューのChildView1に遷移したとき
ナビゲーションパスを配列で管理できるようになると、その変更を監視してナビゲーションを手動で制御可能になります。
方法2でナビゲーションを手動制御する
方法2を前提として、ナビゲーションを手動制御する方法です。
ポイントは以下のとおりです。
- ナビゲーションパスの配列
navigatonPath
に遷移先のデータを手動でスタックする -
NavigationStack(path:root:)
でナビゲーションを管理する -
navigationDestination(for:destination:)
で遷移先のビューを指定する
以下の例では、NavigationLinkの代わりにButtonを使用しています。
ボタン押下時にナビゲーションパスの配列に遷移先のデータを手動でスタックしています。
import SwiftUI
struct ParentView: View {
@State private var navigationPath: [String] = []
var body: some View {
NavigationStack(path: $navigationPath) {
List {
// NavigationLink("ChildView1", value: "1")
Button("ChildView1") {
showChildView(value: "1")
}
// NavigationLink("ChildView2", value: "2")
Button("ChildView2") {
showChildView(value: "2")
}
// NavigationLink("ChildView3", value: "3")
Button("ChildView3") {
showChildView(value: "3")
}
}
.navigationTitle("Parent View")
.navigationDestination(for: String.self) { value in
ChildView(data: value, navigationPath: $navigationPath)
}
}
}
private func showChildView(value: String) {
navigationPath.append(value)
}
}
struct ChildView: View {
let data: String
@Binding var navigationPath: [String]
var body: some View {
Button("トップに戻る") {
navigationPath.removeAll()
}
.navigationTitle("\("ChildView" + data)")
}
}
子ビューのChildViewでは、「トップに戻る」ボタンでナビゲーションパスの要素をクリアすることで、ナビゲーションの状態を変更しています。
まとめ
- 方法1: NavigationLinkに遷移先のViewを紐づけて直接的にナビゲーションを実行
- 方法2: NavigationLinkにデータを紐づけて間接的にナビゲーションを実行する
- 方法2はナビゲーションパスを記録することが容易になる
- 方法2でナビゲーションを手動制御する
Discussion