【Swift UI】NavigationLinkの使い方!navigationDestinationとの組み合わせ

【Swift UI】NavigationLinkの使い方!navigationDestinationとの組み合わせ

この記事からわかること

  • SwiftUINavigationLinkとは?
  • 画面遷移実装する方法
  • 複数あるイニシャライザの使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

SwiftUIでビューをスタック構造で管理するためのNavigationStackにおいて画面遷移に欠かせないNavigationLinkの使い方をまとめていきます。

NavigationLink構造体とは?

公式リファレンス:NavigationLink構造体

struct NavigationLink<Label, Destination> where Label : View, Destination : View

NavigationLinkナビゲーションプレゼンテーションを制御する機能を提供する構造体です。ユーザーがボタンやテキストなどをタップした際に任意の画面に遷移させることができます。遷移機能を有効にするためにはNavigationStackで囲われている必要があります。

インスタンス化時の引数として必要になるのはリンクの文字となるLabel遷移先を示すViewです。

使い方

実際の使い方を見てみます。最初の引数にString型を渡し、クロージャーの中に遷移先のビューを渡します。

NavigationLink("MyNextView") {
    MyNextView()
}

これでボタンをクリックすると画面が遷移するリンクボタンを作成できます。

【Swift UI】NavigationLinkの使い方!navigationDestinationとの組み合わせ

複数あるイニシャライザ

イニシャライザが複数用意されているので書式が様々あります。

NavigationLink {
    MyNextView()
} label: {
    Label("MyNextView", systemImage: "folder")
}
【Swift UI】NavigationLinkの使い方!navigationDestinationとの組み合わせ

変数の値に応じて画面遷移を動作させる

リンクボタンではなく、変数の値の変化に応じて画面遷移を動作させるにはinit(_:destination:isActive:)を使用します。

struct TestNavigationView: View {
    @State  private var isActive: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
                    destination: MyNextView(),
                    isActive: $isActive,
                    label: { EmptyView() }
                )
                
                Button {
                    isActive = true
                } label: {
                    Text("Navigate Button")
                }
            }
            .navigationTitle("Navigation")
        }
    }
}
struct MyNextView: View {
    var body: some View {
        Text("MyNextView")
    }
}

ですがiOS13〜16以降では非推奨となっているのでnavigationDestinationへの置き換えが推奨されています。

struct TestNavigationView: View {
    @State  private var isPresented: Bool = false
    var body: some View {
        NavigationStack {
            Button {
                isPresented = true
            } label: {
                Text("MyNextView")
            }.navigationDestination(isPresented: $isPresented) {
                MyNextView()
            }
        }
    }
}

遷移先ごとに渡す値を変化させる

navigationDestinationinit(_:value:)を使用することで遷移先ごとに渡す値を変化させることができます。

struct TestNavigationView: View {
    
    var body: some View {
        NavigationStack {
            List {
                NavigationLink("Mint", value: Color.mint)
                NavigationLink("Pink", value: Color.pink)
                NavigationLink("Teal", value: Color.teal)
            }
            .navigationDestination(for: Color.self) { color in
                ColorDetail(color: color)
            }
            .navigationTitle("Colors")
        }
    }
}

struct ColorDetail: View {
    var color: Color
    var body: some View {
        Text("ThisColor")
            .foregroundColor(color)
    }
}

この場合はnavigationDestination(for:)を使用することでクリックされたNavigationLinkの値を受け取ることができます。そのためにはforの引数には受け取るデータ型を明示的に指定する必要があります。

func navigationDestination<D, C>(for data: D.Type, @ViewBuilder  destination: @escaping (D) -> C) -> some View where D : Hashable, C : View

公式リファレンス:navigationDestination(for:)

リンクの有効/無効を切り替える

リンクボタンは有効/無効を切り替えることもできます。無効にするにはNavigationLink構造体にたいしてdisabled(true)を指定します。

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Text("要素1")
                Text("要素2")
                NavigationLink("リンク1") {
                    ChildView()
                }.disabled(true) // 無効にする
            } .navigationTitle("タイトル")
        }
    }
}

Do not put a navigation destination modifier inside a "lazy” container, like List or LazyVStack. These containers create child views only when needed to render on screen.

navigationDestinationを使用していると以下のようなエラーがログエリアに出力されることがあります。

Do not put a navigation destination modifier inside a "lazy” container, like List or LazyVStack. These containers create child views only when needed to render on screen. Add the navigation destination modifier outside these containers so that the navigation stack can always see the destination.

これはiOS17以降から発生するようになったエラーで「ListやLazyVStackなどの遅延コンテナの中でnavigationDestinationを書いてはいけない」というエラーのようです。遅延コンテナは表示されるときに初めて子ビューを作るので、要素が多く初期表示外の要素に指定してしまと「遷移先を把握できない」状態になります。

NavigationStack {
    List {
        Button("購入画面へ") {
            isPurchasePresented = true
        } 
        // ❌ NG: List 内に navigationDestination を書かない
        .navigationDestination(isPresented: $isPurchasePresented) {
            InAppPurchaseView()
        }
    }
    // ここに記述するのが正しい
    // .navigationDestination
}

そのためListなどの中ではなく、外側にnavigationDestinationを記述するのが正しい記法のようです。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

Search Box

Sponsor

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index