NavigationStack 的导航方式主要分为两种:带 path 的程序化导航和不带 path 的传统导航。让我详细解释这两种方式及其组合使用:
1. 不使用 Path 的传统导航 #
struct BasicNavigation: View {
var body: some View {
NavigationStack {
List {
// 方式1:使用 value
NavigationLink(value: "Detail 1") {
Text("Go to Detail 1")
}
// 方式2:直接使用 destination
NavigationLink(destination: DetailView(text: "Detail 2")) {
Text("Go to Detail 2")
}
// 方式3:使用 value 传递复杂类型
NavigationLink(value: User(name: "John", id: 1)) {
Text("Go to User Profile")
}
}
.navigationDestination(for: String.self) { text in
Text(text)
}
.navigationDestination(for: User.self) { user in
UserProfileView(user: user)
}
}
}
}
struct User: Hashable {
let name: String
let id: Int
}
2. 使用 Path 的程序化导航 #
struct ProgrammaticNavigation: View {
@State private var path: NavigationPath = NavigationPath()
// 或者使用具体类型的数组
// @State private var path: [String] = []
var body: some View {
NavigationStack(path: $path) {
List {
// 方式1:使用 value
NavigationLink(value: "Detail 1") {
Text("Go to Detail 1")
}
// 方式2:按钮触发程序化导航
Button("Push Multiple Views") {
path.append("First")
path.append("Second")
path.append(User(name: "John", id: 1))
}
}
.navigationDestination(for: String.self) { text in
Text(text)
}
.navigationDestination(for: User.self) { user in
UserProfileView(user: user)
}
.toolbar {
Button("Clear Path") {
path.removeLast()
// 或清除所有
// path = NavigationPath()
}
}
}
}
}
3. 混合使用场景 #
struct HybridNavigation: View {
@State private var path: [NavigationType] = []
enum NavigationType: Hashable {
case detail(String)
case user(User)
case settings
}
var body: some View {
NavigationStack(path: $path) {
List {
// 1. 传统导航链接
NavigationLink("Settings Direct") {
SettingsView()
}
// 2. 使用 value 的导航链接
NavigationLink(value: NavigationType.detail("Detail Info")) {
Text("Go to Detail")
}
// 3. 程序化导航按钮
Button("Complex Navigation") {
path = [
.detail("First"),
.user(User(name: "John", id: 1)),
.settings
]
}
}
.navigationDestination(for: NavigationType.self) { type in
switch type {
case .detail(let text):
DetailView(text: text)
case .user(let user):
UserProfileView(user: user)
case .settings:
SettingsView()
}
}
}
}
}
4. 实际应用示例 #
// 定义导航路径类型
enum AppRoute: Hashable {
case productList
case productDetail(Product)
case cart
case checkout
case orderConfirmation(OrderID)
}
struct ShopAppView: View {
@State private var path: [AppRoute] = []
@StateObject private var shopViewModel = ShopViewModel()
var body: some View {
NavigationStack(path: $path) {
List {
// 1. 直接导航到购物车
NavigationLink(destination: CartView()) {
Text("Shopping Cart")
}
// 2. 产品列表
ForEach(shopViewModel.products) { product in
NavigationLink(value: AppRoute.productDetail(product)) {
ProductRowView(product: product)
}
}
// 3. 快速结账按钮
Button("Quick Checkout") {
path = [.cart, .checkout]
}
}
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .productList:
ProductListView()
case .productDetail(let product):
ProductDetailView(product: product)
case .cart:
CartView()
case .checkout:
CheckoutView()
case .orderConfirmation(let orderID):
OrderConfirmationView(orderID: orderID)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Clear") {
path.removeAll()
}
}
}
}
.environmentObject(shopViewModel)
}
}
// 支持视图
struct ProductDetailView: View {
@EnvironmentObject var shopViewModel: ShopViewModel
let product: Product
var body: some View {
VStack {
// 产品详情
Text(product.name)
// 添加到购物车按钮
Button("Add to Cart") {
shopViewModel.addToCart(product)
}
}
}
}
// 视图模型
class ShopViewModel: ObservableObject {
@Published var products: [Product] = []
@Published var cart: [Product] = []
func addToCart(_ product: Product) {
cart.append(product)
}
}
5. 最佳实践 #
- 选择导航方式的原则:
// 1. 简单单层导航:使用传统 NavigationLink
NavigationLink("Settings") {
SettingsView()
}
// 2. 需要程序化控制的导航:使用 path
NavigationLink(value: AppRoute.settings) {
Text("Settings")
}
// 3. 复杂导航流程:结合使用
- 类型安全的导航:
// 使用枚举定义导航路径
enum NavigationType: Hashable {
case detail(String)
case settings
case profile(User)
}
// 而不是使用原始类型
// @State private var path: [String] // 不推荐
- 深层链接支持:
// 支持从外部URL或通知打开特定页面
func handleDeepLink(_ url: URL) {
// 解析URL
let route = parseDeepLink(url)
// 设置导航路径
path = [route]
}
通过这种方式,你可以灵活地组合使用这两种导航方式,根据具体需求选择最合适的实现方案。