【Swift UI】NavigationLinkの使い方!navigationDestinationとの組み合わせ
この記事からわかること
- SwiftUIのNavigationLinkとは?
- 画面遷移を実装する方法
- 複数あるイニシャライザの使い方
index
[open]
\ アプリをリリースしました /
環境
- Xcode:16.3
- iOS:18.4
- Swift:6
- macOS:Sequoia 15.6.1
SwiftUIでビューをスタック構造で管理するためのNavigationStackにおいて画面遷移に欠かせないNavigationLinkの使い方をまとめていきます。
NavigationLink構造体とは?
struct NavigationLink<Label, Destination> where Label : View, Destination : View
NavigationLinkはナビゲーションプレゼンテーションを制御する機能を提供する構造体です。ユーザーがボタンやテキストなどをタップした際に任意の画面に遷移させることができます。遷移機能を有効にするためにはNavigationStackで囲われている必要があります。
インスタンス化時の引数として必要になるのはリンクの文字となるLabelと遷移先を示すViewです。
使い方
実際の使い方を見てみます。最初の引数にString型を渡し、クロージャーの中に遷移先のビューを渡します。
NavigationLink("MyNextView") {
MyNextView()
}
これでボタンをクリックすると画面が遷移するリンクボタンを作成できます。
複数あるイニシャライザ
イニシャライザが複数用意されているので書式が様々あります。
NavigationLink {
MyNextView()
} label: {
Label("MyNextView", systemImage: "folder")
}
変数の値に応じて画面遷移を動作させる
リンクボタンではなく、変数の値の変化に応じて画面遷移を動作させるには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()
}
}
}
}
遷移先ごとに渡す値を変化させる
navigationDestinationとinit(_: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を記述するのが正しい記法のようです。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。





