SwiftUI NavigationStack 導航教學
在 App 中,最常見的頁面切換方式就是「推入 (Push)」和「返回 (Pop)」。在 SwiftUI 中,我們使用 NavigationStack (iOS 16+) 來管理這種堆疊式的導航。
注意:iOS 16 之前使用的是
NavigationView。雖然目前尚未完全移除,但 Apple 強烈建議新專案使用 NavigationStack,因为它解決了舊版無法精確控制路由的問題。基礎導航 (Basic Navigation)
要啟用導航功能,首先要在最外層包一個 NavigationStack。然後使用 NavigationLink 來建立跳轉按鈕。
struct ContentView: View {
var body: some View {
NavigationStack {
VStack(spacing: 20) {
Text("這是首頁")
// 簡單的跳轉,直接指定目標 View
NavigationLink("前往詳情頁") {
DetailView()
}
}
.navigationTitle("首頁") // 設定導航列標題
.navigationBarTitleDisplayMode(.large) // 標題樣式 (large/inline)
}
}
}
struct DetailView: View {
var body: some View {
Text("這是詳情頁")
.navigationTitle("詳情")
}
}
數值驅動導航 (Value-Based Navigation)
這是 NavigationStack 最強大的功能。傳統的 NavigationView 需要在每個 Link 裡寫死目標 View,導致程式碼耦合且難以管理。
現在,我們可以將「導航邏輯」和「UI」分離:
NavigationLink只負責傳遞 資料 (Value)。.navigationDestination負責決定看到該資料時要顯示什麼頁面。
struct ContentView: View {
let fruits = ["Apple", "Banana", "Cherry"]
var body: some View {
NavigationStack {
List(fruits, id: \.self) { fruit in
// 這裡只傳遞字串 "Apple", "Banana"...
NavigationLink(fruit, value: fruit)
}
.navigationDestination(for: String.self) { fruitName in
// 這裡統一處理:只要收到 String,就跳轉到 FruitDetailView
FruitDetailView(name: fruitName)
}
}
}
}
這在處理複雜列表時非常有用,你不需要在 List 的每一行都實例化目標 View。
程式化導航 (Programmatic Navigation)
如果我們不是點擊按鈕跳轉,而是達到某個條件自動跳轉(例如登入成功後),或是需要一次返回多層 (Pop to Root),這時就需要管理 Path (路徑)。
我們使用一個 NavigationPath 或 [Data] 陣列來綁定 NavigationStack。
struct ContentView: View {
// 綁定路徑,這代表當前的堆疊狀態
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
VStack {
Button("隨機推薦") {
// 通過程式碼加入路徑,會自動跳轉
path.append("Grape")
}
}
.navigationDestination(for: String.self) { bg in
ColorDetail(colorName: bg, path: $path)
}
}
}
}
struct ColorDetail: View {
let colorName: String
@Binding var path: NavigationPath // 接收 path 以便控制返回
var body: some View {
VStack {
Text("現在位置: \(colorName)")
Button("回到首頁 (Pop to Root)") {
// 清空路徑,就是回到最上層
path = NavigationPath()
}
}
}
}
自定義導航列 (Navigation Bar)
我們可以客製化導航列的標題、按鈕等。
.navigationTitle("設定")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("儲存") { save() }
}
ToolbarItem(placement: .topBarLeading) {
Button("取消") { cancel() }
}
}
// 隱藏返回按鈕 (慎用)
.navigationBarBackButtonHidden(true)
總結
- 使用
NavigationStack包裹最外層。 - 簡單跳轉用
NavigationLink("Title") { Destination() }。 - 列表與複雜跳轉推薦用
.navigationDestination(for:)分離資料與視圖。 - 需要通過程式碼控制 (如回首頁) 時,使用
pathBinding。