SwiftUI中子视图调用类方法后主视图未更新值的问题求助
SwiftUI ObservableObject 数据更新后视图不刷新的解决方案
问题根源
你代码里的核心问题是主视图ContentView和子视图UpgradesView各自创建了独立的GameState实例。子视图调用doublePoints更新的是自己的gameState对象,而主视图绑定的是另一个完全无关的实例,所以主视图无法感知到数据变化——控制台打印的是子视图实例的points值,自然和主视图显示的内容无关。
解决方案:共享同一个GameState实例
下面提供两种常用的实现方式,选一种适合你的场景即可:
方式1:直接通过参数传递实例
这是最简单的方式,适合子视图层级不多的情况:
- 修改
ContentView中UpgradeView的调用,把当前的gameState传进去:
TabView { UpgradeView(gameState: gameState) // 传递共享实例 .tabItem { Image(systemName: "rectangle.and.hand.point.up.left.fill") Text("Upgrades") } // 其他tab内容不变 }
- 修改
UpgradesView,移除自身的GameState初始化,改为接收外部传入的实例:
struct UpgradesView: View { @ObservedObject var gameState: GameState // 仅声明,不初始化 var body: some View { // 原有按钮代码保持不变 HStack { Button(action: { gameState.doublePoints() }) { Image(systemName: "arrow.up.circle.fill") .font(.system(size: 40)) } } } } // 预览需要传入实例 struct UpgradesView_Previews: PreviewProvider { static var previews: some View { UpgradesView(gameState: GameState()) } }
方式2:使用@EnvironmentObject实现全局共享
如果你的应用中有多个视图需要访问GameState,用环境对象可以避免层层传递参数:
- 在App入口注入
GameState(以iOS 14+的App结构为例):
@main struct YourAppName: App { @StateObject var gameState = GameState() // iOS 14+用@StateObject管理全局ObservableObject var body: some Scene { WindowGroup { ContentView() .environmentObject(gameState) // 注入到环境中 } } }
- 修改
ContentView,通过@EnvironmentObject获取实例:
struct ContentView: View { @EnvironmentObject var gameState: GameState // 替换原有的@ObservedObject初始化 var body: some View { // 原有视图代码保持不变 } } // 预览需要添加环境对象 struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .environmentObject(GameState()) } }
- 修改
UpgradesView,同样通过@EnvironmentObject获取实例:
struct UpgradesView: View { @EnvironmentObject var gameState: GameState // 替换原有的@ObservedObject初始化 var body: some View { // 原有按钮代码保持不变 } } // 预览需要添加环境对象 struct UpgradesView_Previews: PreviewProvider { static var previews: some View { UpgradesView() .environmentObject(GameState()) } }
额外优化:确保UI更新在主线程
虽然Timer.scheduledTimer默认在主线程执行,但为了避免意外的线程问题,建议在更新@Published属性时明确切换到主线程:
func doublePoints() { var runCount = 0 Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timerEnd in DispatchQueue.main.async { // 确保在主线程更新UI相关属性 self.points += self.pointsPerSecond print(self.points) } runCount += 1 if runCount == 30 { timerEnd.invalidate() } }) }
总结
只要让所有需要访问GameState的视图共享同一个实例,@Published属性的变化就能正确通知到所有绑定的视图,实现实时刷新。
内容的提问来源于stack exchange,提问作者Shaun
相关产品推荐
相关产品推荐

