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

@ObservedObject绑定后UI未更新问题求助

UI未更新的原因及解决方案

问题根源

你当前的代码存在实例不一致的核心问题:

  • 在watchkitapp_Watch_AppApp中,通过@StateObject private var manager = WorkoutTracking()创建了一个WorkoutTracking实例,并注入到环境中,ContentView绑定观察的是这个实例的heartRate。
  • 但在ContentView的初始化方法、按钮点击事件,以及WorkoutTracking内部更新心率的逻辑里,实际操作的是WorkoutTracking.shared这个单例实例。

直白说:UI绑定的是A实例,而更新心率的是B实例,A实例的heartRate从未发生变化,自然不会触发UI刷新。控制台打印的是B实例的心率值,这就是为什么控制台有更新但UI毫无反应。

解决方案

统一使用同一个实例即可,以下是两种可行方案:

方案一:统一使用单例作为环境对象

修改App入口的@StateObject初始化逻辑,直接复用单例:

@main
struct watchkitapp_Watch_AppApp: App {
    @StateObject private var manager = WorkoutTracking.shared
    @SceneBuilder var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(manager)
        }
    }
}

然后修改ContentView中所有调用WorkoutTracking.shared的地方,替换为环境对象的manager:

struct ContentView: View {
    @EnvironmentObject var manager: WorkoutTracking
    
    init() {
        print("AWAKE")
        WorkoutTracking.authorizeHealthKit()
        // 替换为manager调用
        manager.startWorkOut()
        WatchKitConnection.shared.startSession()
    }
    
    var body: some View {
        VStack {
            Text("Live heart rate: \(manager.heartRate)")
            
            Button {
                print("Start workout session!")
                manager.startWorkOut()
            } label: {
                Text("Start Workout")
            }.padding()
            
            Button {
                print("Stop workout session!")
                manager.stopWorkOut()
            } label: {
                Text("Stop Workout")
            }.padding()
        }
    }
}

方案二:移除单例,完全依赖SwiftUI环境注入(推荐)

如果不需要全局单例,更符合SwiftUI的设计模式,建议移除WorkoutTracking中的单例定义:

class WorkoutTracking: NSObject, ObservableObject {
    // 删除单例定义:static let shared = WorkoutTracking()
    let healthStore = HKHealthStore()
    let configuration = HKWorkoutConfiguration()
    var workoutSession: HKWorkoutSession!
    var workoutBuilder: HKLiveWorkoutBuilder!
    @Published var heartRate: Double
    
    weak var delegate: WorkoutTrackingDelegate?
    
    override init() {
        self.heartRate = 0.0
        super.init()
    }
}

确保所有操作都通过环境对象的manager实例执行,这样就能保证UI观察的实例和更新的实例完全一致。

额外注意点

  • 确认authorizeHealthKit()是类方法,如果是实例方法,需改为manager.authorizeHealthKit()调用。
  • 你代码中用DispatchQueue.main.async更新heartRate的逻辑是正确的,UI状态更新必须在主线程执行,这部分无需修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:08:18