複数階層のナビゲーション
アプリでは複数の階層を持つナビゲーション構造が必要になることがあります。NavigationStack で深い階層のナビゲーションを実装する方法を解説します。
階層的なナビゲーション
NavigationLink を連続して使用することで、複数階層のナビゲーションを構築できます。
struct ContentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink("カテゴリA") {
CategoryView(name: "カテゴリA")
}
NavigationLink("カテゴリB") {
CategoryView(name: "カテゴリB")
}
}
.navigationTitle("ホーム")
}
}
}
struct CategoryView: View {
let name: String
var body: some View {
List {
NavigationLink("サブカテゴリ1") {
SubCategoryView(name: "サブカテゴリ1")
}
NavigationLink("サブカテゴリ2") {
SubCategoryView(name: "サブカテゴリ2")
}
}
.navigationTitle(name)
}
}
struct SubCategoryView: View {
let name: String
var body: some View {
List {
NavigationLink("アイテム1") {
ItemDetailView(name: "アイテム1")
}
NavigationLink("アイテム2") {
ItemDetailView(name: "アイテム2")
}
}
.navigationTitle(name)
}
}
struct ItemDetailView: View {
let name: String
var body: some View {
Text("\(name)の詳細")
.navigationTitle(name)
}
}navigationDestination を使った階層管理
値ベースのナビゲーションを使うと、遷移先の定義を一箇所にまとめられます。
enum Route: Hashable {
case category(String)
case subCategory(String)
case item(String)
}
struct ContentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink("カテゴリA", value: Route.category("A"))
NavigationLink("カテゴリB", value: Route.category("B"))
}
.navigationDestination(for: Route.self) { route in
switch route {
case .category(let name):
CategoryRouteView(category: name)
case .subCategory(let name):
SubCategoryRouteView(subCategory: name)
case .item(let name):
ItemRouteView(item: name)
}
}
.navigationTitle("ホーム")
}
}
}階層間でのデータ受け渡し
深い階層にデータを渡す方法はいくつかあります。
直接渡す
各画面のイニシャライザでデータを受け取る。シンプルだが、階層が深いとバケツリレーになる。
Environment
@EnvironmentObject や @Environment でデータを共有。どの階層からもアクセス可能。
path で状態管理
NavigationPath に必要な情報をすべて含めて管理する。
// EnvironmentObject を使った例
class AppState: ObservableObject {
@Published var selectedCategory: String?
@Published var cart: [String] = []
}
struct ContentView: View {
@StateObject private var appState = AppState()
var body: some View {
NavigationStack {
CategoryListView()
}
.environmentObject(appState)
}
}
struct ItemDetailView: View {
let item: String
@EnvironmentObject var appState: AppState
var body: some View {
VStack {
Text(item)
Button("カートに追加") {
appState.cart.append(item)
}
}
}
}任意の階層に直接遷移
path を使えば、途中の画面をスキップして深い階層に直接遷移できます。
struct ContentView: View {
@State private var path: [Route] = []
var body: some View {
NavigationStack(path: $path) {
VStack {
Button("アイテム詳細に直接遷移") {
// 階層を一気に構築
path = [
.category("A"),
.subCategory("Sub1"),
.item("Item1")
]
}
}
.navigationDestination(for: Route.self) { route in
// ... 遷移先の定義
}
}
}
}通常の階層遷移
ユーザーが1画面ずつ進んでいく。戻るボタンで1つずつ戻る。
ディープリンク的な遷移
path を操作して任意の階層に直接ジャンプ。通知からの遷移などに便利。
複数階層のナビゲーションでは、画面間の依存関係を整理し、適切なデータ管理方法を選択することが重要です。