SwiftUI NavigationStackの使い方【実用編】

ここではNavigationStackについて少し詳細にリストの表示から複数のページへ遷移する方法などを紹介していきたいと思います。
基本的な使用方法について知りたいって人は以下のページで紹介していますのでそちらを参考にしてみてください。
SwiftUIのiOS16以降で導入されたNavigationStackは、従来のNavigationViewの後継となる新しいナビゲーションコンポーネントです。従来よりも柔軟で管理しやすくなっています。
主な違いとしてNavigationStackが遷移状態を配列で管理でき、データ駆動型のナビゲーションやディープリンクに対応しやすくなった点が挙げられます。ボタンタップで子画面表示
ボタンタップで子画面表示(遷移状態管理にNavigationPathを使用)
Case1 ボタンタップで子画面表示
遷移状態の管理にNavigationPathを使用しButtonタップでNavigationPathの配列にappendでInt値を設定することで.navigationDestinationで定義した画面へ遷移します。
/// ボタンタップで子画面表示
/// NavigationPathを使用しpathに値設定で.navigationDestination で子画面表示
struct NavigationStackSample_Case1_1: View {
@State var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
Button("遷移") {
// タップでpathにInt0を設定
path.append(1)
}
// ナビゲーション修飾子
.navigationTitle("ホーム")
.navigationBarTitleDisplayMode(.inline)
// pathにIntがappendされた時に遷移するView
.navigationDestination(for: Int.self, destination: { value in
Text("子画面(\(value))")
})
}
}
}
ポイント: pass
に Intの値
をappend
することによりnavigationDestinationで定義した子画面へ遷移します。
ボタンタップで子画面表示(遷移状態管理にInt型配列を使用)
Case2 ボタンタップで子画面表示
遷移状態の管理にInt型の配列を使用しIntに値設定で.navigationDestinationで子画面表示
NavigationPathを使用しButtonタップでNavigationPathの配列にappendでInt値を設定することで.navigationDestinationで定義した画面へ遷移します。
/// ボタンタップで子画面表示
/// Int配列を使用しIntに値設定で.navigationDestinationで子画面表示
/// 遷移先への引数型が1パターンの場合はNavigationPathではなく
/// 使用する引数の型でも遷移できる
struct NavigationStackSample_Case1_2: View {
@State var transition: [Int] = []
var body: some View {
NavigationStack(path: $transition) {
Button("遷移") {
// タップでpathにInt0を設定
transition.append(1)
}
// ナビゲーション修飾子
.navigationTitle("ホーム")
.navigationBarTitleDisplayMode(.inline)
// pathにIntがappendされた時に遷移するView
.navigationDestination(for: Int.self, destination: { value in
Text("子画面(\(value))")
})
}
}
}
ポイント: 遷移先への引数型が1パターンの場合はNavigationPathではなく使用する引数の型(上の例ではInt型を使用)でも遷移できる
リストタップで子画面表示
Case3 リストボタンタップで子画面表示
遷移状態の管理にInt型の配列を使用しIntに値設定で.navigationDestinationで子画面表示
NavigationPathを使用しButtonタップでNavigationPathの配列にappendでInt値を設定することで.navigationDestinationで定義した画面へ遷移します。
/// リストタップで子画面表示
/// pathに値設定で.navigationDestinationで子画面表示
struct NavigationStackSample_Case2: View {
@State var path = NavigationPath()
var body: some View {
// pathによる遷移を行うNavigationStack
NavigationStack(path: $path) {
List {
Button("遷移 ページ1") {
path.append(1)
}
Button("遷移 ページ2") {
path.append(2)
}
Button("遷移 ページ3") {
path.append(3)
}
}
// ナビゲーション修飾子
.navigationTitle("ホーム")
.navigationBarTitleDisplayMode(.inline)
.navigationDestination(for: Int.self, destination: { appended in
Text("ページ:\(appended)")
})
}
}
}
リストタップで子画面表示 NavigationLink を使用
Case4 NavigationLink によりリストボタンを設定し、タップで子画面(クロージャ)へ遷移します。
/// リストタップで子画面表示
/// NavigationLink による子画面(クロージャ)表示
struct NavigationStackSample_Case3: View {
var body: some View {
NavigationStack {
List {
NavigationLink("子画面へ遷移") {
Text("子画面")
}
}
// ナビゲーション修飾子
.navigationTitle("ホーム")
}
}
}
ポイント:.navigationDestinationではなくNavigationLinkを使用しての小画面へ遷移します。
リストタップで子画面表示 pathに値設定で型別に定義した.navigationDestinationで子画面表示
遷移状態の管理にNavigationPathを使用し.navigationDestinationで入力型別に画面を定義し、ButtonタップでNavigationPathの配列に表示させたい画面の入力型をappendすることで指定した画面へ遷移します。
/// リストタップで子画面表示
/// pathに値設定で型別に定義した.navigationDestinationで子画面表示
struct NavigationStackSample_Case4: View {
@State var path = NavigationPath()
var body: some View {
// pathによる遷移を行うNavigationStack
NavigationStack(path: $path) {
List {
Button("遷移 ページ1") {
path.append(1)
}
Button("遷移 ページ2") {
path.append(2)
}
Button("遷移 ページ3") {
path.append(3)
}
Button("遷移 ページ4") {
path.append("文字")
}
}
// ナビゲーション修飾子
.navigationTitle("ホーム")
.navigationBarTitleDisplayMode(.inline)
.navigationDestination(for: Int.self, destination: { appended in
Text("ページ:\(appended)")
})
.navigationDestination(for: String.self, destination: { appended in
Text("文字列ページ:\(appended)")
})
}
}
}
複数ページ&複数階層の画面遷移
応用として、画面に階層化し各階層への遷移を管理を少ないコードで実装することができる
Home画面にリストボタンでページA〜ページEへの遷移ボタンを準備し以下のように階層遷移を構成する。
ページAはページBへ遷移可能
ページBはページCへ遷移可能
ページCはページDへ遷移可能
ページDはページEへ遷移可能
ページEは戻るだけ可
- ページAはページBへ遷移可能
- ページBはページCへ遷移可能
- ページCはページDへ遷移可能
- ページEは戻るだけ可可能
- ページAはページBへ遷移可能
遷移イメージ
/// 複数階層の画面遷移
///
///
enum PageName {
case pageA, pageB, pageC, pageD, pageE
}
struct NavigationStackSample_Case5: View {
@State var path = NavigationPath()
var body: some View {
// pathによる遷移を行うNavigationStack
NavigationStack(path: $path) {
List {
Button("遷移 ページA") {
path.append(PageName.pageA)
}
Button("遷移 ページB") {
path.append(PageName.pageB)
}
Button("遷移 ページC") {
path.append(PageName.pageC)
}
Button("遷移 ページD") {
path.append(PageName.pageD)
}
Button("遷移 ページE") {
path.append(PageName.pageE)
}
}
// ナビゲーション修飾子
.navigationTitle("ホーム")
.navigationBarTitleDisplayMode(.inline)
.navigationDestination(for: PageName.self, destination: { value in
switch value {
case .pageA :
PageAView(path: $path)
// ナビゲーション修飾子 ページタイトル設定
.navigationTitle("ページA")
case .pageB:
PageBView(path: $path)
// ナビゲーション修飾子 ページタイトル設定
.navigationTitle("ページB")
case .pageC:
PageCView(path: $path)
// ナビゲーション修飾子 ページタイトル設定
.navigationTitle("ページC")
case .pageD:
PageDView(path: $path)
// ナビゲーション修飾子 ページタイトル設定
.navigationTitle("ページD")
case .pageE:
PageEView(path: $path)
// ナビゲーション修飾子 ページタイトル設定
.navigationTitle("ページE")
}
})
}
}
}
struct PageAView: View {
@Binding var path: NavigationPath
var body: some View {
List {
Button("B画面へ遷移") {
path.append(PageName.pageB)
}
}
}
}
struct PageBView: View {
@Binding var path: NavigationPath
var body: some View {
List {
Button("C画面へ遷移") {
path.append(PageName.pageC)
}
}
}
}
struct PageCView: View {
@Binding var path: NavigationPath
var body: some View {
List {
Button("D画面へ遷移") {
path.append(PageName.pageD)
}
}
}
}
struct PageDView: View {
@Binding var path: NavigationPath
var body: some View {
List {
Button("E画面へ遷移") {
path.append(PageName.pageE)
}
}
}
}
struct PageEView: View {
@Binding var path: NavigationPath
var body: some View {
List {
Button("終了") {
path.removeLast()
}
}
}
}
まとめ
NavigationStackは、従来のNavigationViewよりも直感的に画面遷移を管理できるのが特徴です。画面遷移ロジックを容易に実装できます。