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

如何强制NavigationLink重新创建目标视图?

解决SwiftUI NavigationLink重复推送旧视图的问题

问题原因

你的代码中,gameView是一个计算属性,SwiftUI会对这类视图进行缓存优化。这就导致每次通过showGameView激活导航链接时,复用的都是同一个视图实例,包括其中的MyGameModel对象,所以关闭游戏视图后再次打开,显示的还是之前的状态。

解决方案

方案1:使用NavigationStack(iOS 16+ 推荐)

iOS 16及以上版本推荐使用NavigationStack替代旧的NavigationView,通过路径管理导航栈,每次向路径中添加新元素时,会自动生成新的视图实例:

struct MenuView: View {
    @State private var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Button {
                    // 每次点击添加新的路径元素,触发新视图创建
                    path.append(())
                } label: {
                    Text("Play")
                }
            }
            .navigationDestination(for: Void.self) {
                let gameModel = MyGameModel.myGameModel()
                ContentView()
                    .environmentObject(gameModel)
            }
        }
    }
}

方案2:给目标视图添加唯一ID

通过给目标视图绑定一个动态变化的id,让SwiftUI认为每次都是新视图。可以用一个计数器来实现:

struct MenuView: View {
    @State private var showGameView: Bool = false
    @State private var gameViewCounter = 0
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
                    destination: gameView.id(gameViewCounter),
                    isActive: $showGameView) {
                        EmptyView()
                    }
                Button {
                    // 先更新计数器,再激活导航
                    gameViewCounter += 1
                    showGameView = true
                } label: {
                    Text("Play")
                }
            }
        }
        .navigationViewStyle(.stack)
    }
    
    @MainActor
    @ViewBuilder private var gameView: some View {
        let gameModel = MyGameModel.myGameModel()
        ContentView()
            .environmentObject(gameModel)
    }
}

方案3:将视图创建改为方法调用

把gameView从计算属性改为方法,这样每次调用方法都会创建新的视图和模型实例:

struct MenuView: View {
    @State private var showGameView: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
                    destination: makeGameView(),
                    isActive: $showGameView) {
                        EmptyView()
                    }
                Button {
                    showGameView = true
                } label: {
                    Text("Play")
                }
            }
        }
        .navigationViewStyle(.stack)
    }
    
    @MainActor
    private func makeGameView() -> some View {
        let gameModel = MyGameModel.myGameModel()
        return ContentView()
            .environmentObject(gameModel)
    }
}

核心原理

以上方案的本质都是让SwiftUI每次触发导航时,识别到目标视图是全新的实例,从而重新初始化视图及其关联的MyGameModel,彻底避免复用旧状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:52:31