現在のSwiftUIだと、NavigationViewではなく、NavigationStack使わなければ、ということなのだが、色々ググってみたものの、いまいち、実践的な実装のサンプルが見つからなかったので、Gemini君と会話して、ようやく、腑に落ちる実装ができたので、下記、書いてみます。
サンプルのイメージとしてはItemオブジェクトがあり、そのItemのリスト表示から、Itemの詳細Viewに展開するといったものです。
ググって出てくる実装例は、NavigationLinkのvalueにItem自体を渡すものなんだけど、同じView上に、Itemとは関連しないViewへのリンク先がある場合、NavigationStackのpathに渡す配列の定義をどうするかってあたりが記述されていなかったりする。
ポイントはpathに渡す配列は、Hashableなenumの配列にするということ。そして、enumのcaseに記述する要素に関連オブジェクトを付加すること。
ということで、下記がサンプルになります。
enum Path: Hashable {
case itemDetail(Item)
case newItem
case about
}
... struct
@State var path: [Path] = []
... body
NavigationStack(path: $path) {
VStack {
List {
ForEach(Items) { item in
NavigationLink(value: Path.targetDetail(item)) {
Text(item.name ?? "")
}
}
}
Spacer()
NavigationLink(value: Path.newItem) {
Image(systemName: "pencil")
}
NavigationLink(value: Path.about) {
Image(systemName: "questionmark")
}
}
.navigationDestination(for: Path.self) { value in
if case Path.targetDetail(let item) = value {
ItemDetailView(item: item, path: $path)
}
if case Path.newItem = value {
NewItemView(path: $path)
}
if case Path.about = value {
AboutView(path: $path)
}
}
}
このサンプルなら、if case じゃなく、本来ならswitchなんだろうけど、ItemDetailViewからのLink先もPathに含む場合は、このView自体には必要ないのに記述しなければいけないので、まあ、if caseにしとくのが良いかと。
ItemDetailViewの中から、別のViewに展開して、その展開先のViewから一気に最初のList画面に戻れるというのが利点ですね。
Button(action: {
path.removeAll()
}, label: {
Image(systemName: "house")
}