You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NavigationStack外部使用NavigationLink实现页面跳转?

解决NavigationStack底部栏NavigationLink无法跳转的问题

问题根源

你添加的底部栏是通过safeAreaInset附着在NavigationStack外部的,这导致栏内的NavigationLink无法获取到导航栈的上下文环境,自然无法触发跳转——只有处于NavigationStack内部的NavigationLink才能和导航栈关联。

最优解决方案:绑定导航路径控制跳转

通过给NavigationStack绑定一个路径变量,用Button替代NavigationLink,手动修改路径来触发页面跳转。这种方式既能让底部栏常驻所有推送视图,又不用在每个子视图里重复写底部栏代码。

修改后的代码示例

// 用字符串标识导航目标,也可以换成自定义枚举更安全
struct ContentView: View {
    @State private var navPath = [String]()
    
    var body: some View {
        NavigationStack(path: $navPath) {
            VStack {
                Image(systemName: "globe")
                Text("Hello, world!")
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    NavigationLink("Button 1", value: "screen1")
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    NavigationLink("Button 2", value: "screen2")
                }
            }
            // 根据路径匹配目标视图
            .navigationDestination(for: String.self) { target in
                switch target {
                case "screen1":
                    Text("Screen 1")
                case "screen2":
                    Text("Screen 2")
                default:
                    Text("未知页面")
                }
            }
        }
        .safeAreaInset(edge: .bottom, spacing: 0) {
            HStack {
                Button("Button 1") {
                    navPath.append("screen1")
                }
                .frame(minWidth: 0, maxWidth: .infinity)
                
                Button("Button 2") {
                    navPath.append("screen2")
                }
                .frame(minWidth: 0, maxWidth: .infinity)
            }
            .padding()
            .background(.regularMaterial)
        }
    }
}

进阶优化:用枚举管理导航目标

为了避免字符串硬编码的错误,推荐用枚举定义所有导航页面,代码更规范易维护:

// 枚举所有导航目标
enum NavTarget: Hashable {
    case screen1
    case screen2
}

struct ContentView: View {
    @State private var navPath = [NavTarget]()
    
    var body: some View {
        NavigationStack(path: $navPath) {
            VStack {
                Image(systemName: "globe")
                Text("Hello, world!")
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    NavigationLink("Button 1", value: NavTarget.screen1)
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    NavigationLink("Button 2", value: NavTarget.screen2)
                }
            }
            .navigationDestination(for: NavTarget.self) { target in
                switch target {
                case .screen1:
                    Text("Screen 1")
                case .screen2:
                    Text("Screen 2")
                }
            }
        }
        .safeAreaInset(edge: .bottom, spacing: 0) {
            HStack {
                Button("Button 1") {
                    navPath.append(.screen1)
                }
                .frame(minWidth: 0, maxWidth: .infinity)
                
                Button("Button 2") {
                    navPath.append(.screen2)
                }
                .frame(minWidth: 0, maxWidth: .infinity)
            }
            .padding()
            .background(.regularMaterial)
        }
    }
}

备选方案(不推荐)

如果不想用路径绑定,也可以把底部栏放到NavigationStack内部的.toolbar(placement: .bottomBar)里,但这种方式需要在每个子视图中重复添加toolbar代码,不符合你“无需重复编写”的需求,所以更推荐上面的路径绑定方案。

内容的提问来源于stack exchange,提问作者TruMan1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:20:46