這個系列與 NavigationStack 將關的文章如下
今天的程式碼會從
Day 21 - 在 SwiftUI 中使用 navigationDestination 封裝開啟畫面
的最終程式碼接續開始進行
struct Flavor: Identifiable, Hashable {
let id = UUID().uuidString
let name: String
}
struct ContentView: View {
private let flavors = ["綠豆", "紅豆", "芋粿", "愛玉"].map(Flavor.init)
var body: some View {
NavigationStack {
ForEach(flavors) { flavor in
NavigationLink(flavor.name, value: flavor).padding()
}
.navigationDestination(for: Flavor.self) { flavor in
Text(flavor.name)
.navigationTitle(flavor.name)
}
}
}
}
init(path: Binding<Data>, root: () -> Root)
Data 必須為一個有序的 collection 例如陣列,而元素必須為 Hashable
以及
init(path: Binding<NavigationPath>, root: () -> Root)
當有多樣性物件的情形就可以直接使用 NavigationPath 。
加入 path 之後,NavigationStack 的畫面堆疊 (stack) 就會跟 path 同步了。
例如往 path 推入一個值, NavigationStack 就會根據 path 變化。步驟如下:
像是 deep link 可以用利用這個機制,從外部傳入網址、經過分析就可以利用這個機制觸發開啟期望的頁面。
不過為了簡化和專注在 path 的用法,今天的範例就先以隨機的方式決定要開啟的畫面吧!
這一篇先用第一個方法,陣列,作為範例
struct ContentView: View {
@State private var path = [Flavor]()
private let flavors = ["綠豆", "紅豆", "芋粿", "愛玉"].map(Flavor.init)
var body: some View {
NavigationStack(path: $path) {
ForEach(flavors) { flavor in
NavigationLink(flavor.name, value: flavor).padding()
}
.navigationDestination(for: Flavor.self) { flavor in
Text(flavor.name)
.navigationTitle(flavor.name)
}
}
}
}
這時候的畫面還沒有變化,畫面操作如下
struct ContentView: View {
private let flavors = ["綠豆", "紅豆", "芋粿", "愛玉"].map(Flavor.init)
@State private var path = [Flavor]()
var body: some View {
NavigationStack(path: $path) {
Button {
if let flavor = flavors.randomElement() {
path.append(flavor)
}
} label: {
Text("好手氣")
}
.foregroundColor(.purple)
.padding()
ForEach(flavors) { flavor in
NavigationLink(flavor.name, value: flavor).padding()
}
.navigationDestination(for: Flavor.self) { flavor in
Text("\(flavor.name)")
.navigationTitle(flavor.name)
}
// 加個標題吧 :)
.navigationTitle("點心")
}
}
}
主要的邏輯在這裡,當點下「好手氣」取得一個隨機的口味,像這樣推入 path 。
path.append(flavor)
在 NavigationStack 接受到 path 更新的事件之後,就會去看 navigationDestination 中 Flavor
的定義,推出一個已經定義好的畫面:
可以從動畫中看到,每一次點擊好手氣出現的口味都不一樣。
在一些情境之內會有像是這樣的需求
先前往一個列表頁面,再把其中一項的詳細列開起來
也可以用 path 達成
同樣的這篇也簡化需求,就以一次推入兩個 flavors 來示範一下:
struct ContentView: View {
private let flavors = ["綠豆", "紅豆", "芋粿", "愛玉"].map(Flavor.init)
@State private var path = [Flavor]()
var body: some View {
NavigationStack(path: $path) {
Button {
if let flavor1 = flavors.randomElement(),
let flavor2 = flavors.randomElement() {
path.append(contentsOf: [flavor1, flavor2])
}
} label: {
Text("好手氣")
}
.foregroundColor(.purple)
.padding()
ForEach(flavors) { flavor in
NavigationLink(flavor.name, value: flavor).padding()
}
.navigationDestination(for: Flavor.self) { flavor in
Text("\(flavor.name)")
.navigationTitle(flavor.name)
}
.navigationTitle("點心")
}
}
}
像是這樣,推入兩個 flavors
path.append(contentsOf: [flavor1, flavor2])
效果如下
最近不斷在尋找 deep link 跳轉的實作方法時就發現 NavigationStack 原來能夠那麼活用,接著應該還會有幾篇以這個需求為中心發布。
以上,那今天的 SwiftUI 的大大小小就到這邊,明天見!
本篇使用到的 UI 元件和 modifiers 基本上沒有受到版本更新影響。若要在 Xcode 14 等環境下使用也是沒問題的。