SwiftUI — NavigationLink

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. 选择导航方式的原则
// 1. 简单单层导航:使用传统 NavigationLink
NavigationLink("Settings") {
    SettingsView()
}

// 2. 需要程序化控制的导航:使用 path
NavigationLink(value: AppRoute.settings) {
    Text("Settings")
}

// 3. 复杂导航流程:结合使用
  1. 类型安全的导航
// 使用枚举定义导航路径
enum NavigationType: Hashable {
    case detail(String)
    case settings
    case profile(User)
}

// 而不是使用原始类型
// @State private var path: [String] // 不推荐
  1. 深层链接支持
// 支持从外部URL或通知打开特定页面
func handleDeepLink(_ url: URL) {
    // 解析URL
    let route = parseDeepLink(url)
    // 设置导航路径
    path = [route]
}

通过这种方式,你可以灵活地组合使用这两种导航方式,根据具体需求选择最合适的实现方案。

本文共 857 字,上次修改于 Jan 5, 2025