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

SwiftUI中子视图调用类方法后主视图未更新值的问题求助

SwiftUI ObservableObject 数据更新后视图不刷新的解决方案

问题根源

你代码里的核心问题是主视图ContentView和子视图UpgradesView各自创建了独立的GameState实例。子视图调用doublePoints更新的是自己的gameState对象,而主视图绑定的是另一个完全无关的实例,所以主视图无法感知到数据变化——控制台打印的是子视图实例的points值,自然和主视图显示的内容无关。

解决方案:共享同一个GameState实例

下面提供两种常用的实现方式,选一种适合你的场景即可:

方式1:直接通过参数传递实例

这是最简单的方式,适合子视图层级不多的情况:

  1. 修改ContentView中UpgradeView的调用,把当前的gameState传进去:
TabView {
    UpgradeView(gameState: gameState) // 传递共享实例
        .tabItem {
            Image(systemName: "rectangle.and.hand.point.up.left.fill")
            Text("Upgrades")
        }
    // 其他tab内容不变
}
  1. 修改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,用环境对象可以避免层层传递参数:

  1. 在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) // 注入到环境中
        }
    }
}
  1. 修改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())
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:31:36