【SwiftUI】ビューコントローラー:NavigationStack

環境

Xcode16.1
Swift:6
iOS16

基本

概要

NavigationStackとは
※iOS16から(それ以前のバージョンではNavigationViewを使用)
階層的な画面遷移を管理するビュー。
NavigationLinkで遷移先を指定する。

import SwiftUI

struct ContentView: View {
    var body: some View {
        /// ナビゲーションの定義
        NavigationStack {
            /// 画面遷移リンクの定義
            NavigationLink(destination: DetailView()) {
                Text("詳細画面へ")
            }
            .navigationTitle("ホーム画面")    // ナビゲーションタイトルの定義
        }
    }
}

/// 遷移先のView定義
struct DetailView: View {
    var body: some View {
        Text("詳細画面です")
    }
}

#Preview {
    ContentView()
}

タイトルの表示モード(navigationBarTitleDisplayMode)

large(デフォルト)

・大きなタイトル表示
・スクロールするとinline表示に切り替わる

    NavigationStack {
     〜〜省略〜〜
        .navigationTitle("ホーム画面")
        .navigationBarTitleDisplayMode(.large)
    }
inline

・ナビゲーションバー領域にタイトルを表示

    NavigationStack {
     〜〜省略〜〜
        .navigationTitle("ホーム画面")
        .navigationBarTitleDisplayMode(.inline)
    }

ツールバー

・画面上部にビューを表示する
・ToolbarItemまたはToolbarItemGroupを使用する
・navigationTitleを使用しなくてもタイトルを表示可能(モディファイアも使用可能)
・navigationTitleを使用しない場合、戻るボタンの名前が「back」になるため「戻る」にカスタマイズしている

<パラメータ>
navigationBarLeading:左側にビューを表示
navigationBarTrailing:右側にビューを表示
principal:中央にビューを表示
bottomBar:下側にビューを表示

import SwiftUI

struct ContentView: View {
    let items = ["アイテム1", "アイテム2", "アイテム3"]

    var body: some View {

        NavigationStack {
            // 別画面へのリンク
            List(items, id: \.self) { item in
                NavigationLink(destination: DetailView(item: item)) {
                    Text(item)
                }
            }
            // タイトルをインライン表示に設定(ツールバー下の余白をなくす)
            .navigationBarTitleDisplayMode(.inline)

            // ツールバー
            .toolbar {
                // 左側(navigationBarLeading)
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: {
                        print("左側")
                    }) {
                        Image(systemName: "message")
                    }
                }

                // 右側(navigationBarTrailing)
                // グループ化すると複数表示できる
                ToolbarItemGroup(placement: .navigationBarTrailing) {
                    Button("Save") {
                        print("Save button tapped")
                    }
                    Button("Edit") {
                        print("Edit button tapped")
                    }
                }

                // 中央(principal)
                ToolbarItem(placement: .principal) {
                    Text("ホーム画面")
                }

                // 下側(bottomBar)
                ToolbarItem(placement: .bottomBar) {
                    Button(action: {
                        print("下側")
                    }) {
                        Image(systemName: "chevron.down")
                    }
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle()) // iPadでもiPhoneと同じスタイルにする
    }
}

// 詳細画面のビュー
struct DetailView: View {
    let item: String
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        VStack {
            Text("\(item)の詳細")
            Spacer()
        }
        .toolbar {
            // ツールバーの左側
            ToolbarItem(placement: .navigationBarLeading) {
                // 自作の「戻る」ボタン
                Button(action: {
                    presentationMode.wrappedValue.dismiss() // 前の画面に戻る
                }) {
                    HStack {
                        Image(systemName: "chevron.left")
                        Text("戻る")
                    }
                }
            }
            // ツールバーの中央
            ToolbarItem(placement: .principal) {
                Text("詳細画面")
            }
        }
        .navigationBarBackButtonHidden(true) // デフォルトの戻るボタンを非表示にする
    }
}

#Preview {
    ContentView()
}

・指定したデータ型の値を受け取ってナビゲーション遷移させる
・データ型ごとに遷移先を指定できる
・forにNavigationLinkのvalueのデータ型を指定する
・データ型の後ろに.selfをつける(例.for: String.self)

import SwiftUI

struct ContentView: View {

    var body: some View {

        NavigationStack {

            // 別画面に渡す値を設定(String型)
            NavigationLink(value: "あいうえお") {
                Text("あいうえお")
            }

            // 別画面に渡す値を設定(Int型)
            NavigationLink(value: 1234) {
                Text("1234")
            }

            // NavigationLinkのvalueのデータ型と格納先を指定(String型)
            .navigationDestination(for: String.self) { strVal in
                // 別画面に値を渡して遷移する
                StrView(item: strVal)
            }

            // NavigationLinkのvalueのデータ型と格納先を指定(int型)
            .navigationDestination(for: Int.self) { intVal in
                // 別画面に値を渡して遷移する
                IntView(item: intVal)
            }

        }
    }
}

// 別画面のビュー(String型)
struct StrView: View {
    let item: String

    var body: some View {
        Text(item)
    }
}
// 別画面のビュー(Int型)
struct IntView: View {
    let item: Int

    var body: some View {
        Text(String(item))
    }
}

#Preview {
    ContentView()
}