SwiftUI扩展内无法获取@Published属性更新的问题求助
问题分析与解决方案
核心问题
- 实例不共享:你在
RecordView中初始化myViewModel时,传入的是新创建的WatchManager实例(ViewModel(watchManager: WatchManager())),而非视图中已有的watchManager对象。这导致ViewModel和视图使用的是两个完全独立的WatchManager,自然无法同步更新。 - 未监听
@Published变化:即使传入了正确的WatchManager实例,你的ViewModel只是把它作为普通属性存储,没有监听其@Published属性的变更,所以deltaDistance更新时,deltaDistance2不会自动同步。
修正方案
步骤1:共享WatchManager实例
将RecordView中myViewModel的初始化改为传入当前的watchManager,避免创建新实例:
struct RecordView: View { @StateObject private var watchManager: WatchManager @StateObject private var myViewModel: ViewModel // 在视图初始化时统一创建并共享实例 init() { let manager = WatchManager() self._watchManager = StateObject(wrappedValue: manager) self._myViewModel = StateObject(wrappedValue: ViewModel(watchManager: manager)) } // 剩余body代码不变 }
步骤2:在ViewModel中监听WatchManager的更新
有两种方式实现自动同步:
方式一:Async/Await 订阅(Swift 5.5+)
extension RecordView { @MainActor class ViewModel: ObservableObject { @ObservedObject var watchManager: WatchManager @Published var deltaDistance2 = 0.0 init(watchManager: WatchManager) { self.watchManager = watchManager // 初始同步值 self.deltaDistance2 = watchManager.deltaDistance // 订阅deltaDistance的变化,自动更新 Task { for await _ in watchManager.$deltaDistance.values { self.deltaDistance2 = watchManager.deltaDistance } } } // 保留手动同步方法(如果需要) func setWithOtherDistance() { deltaDistance2 = watchManager.deltaDistance } } }
方式二:Combine 订阅
import Combine extension RecordView { @MainActor class ViewModel: ObservableObject { private var watchManager: WatchManager @Published var deltaDistance2 = 0.0 private var cancellables = Set<AnyCancellable>() init(watchManager: WatchManager) { self.watchManager = watchManager self.deltaDistance2 = watchManager.deltaDistance // 绑定deltaDistance的变化到deltaDistance2 watchManager.$deltaDistance .assign(to: \.deltaDistance2, on: self) .store(in: &cancellables) } func setWithOtherDistance() { deltaDistance2 = watchManager.deltaDistance } } }
完整修正代码
import SwiftUI import Combine // 使用Combine方式时需要导入 struct RecordView: View { @StateObject private var watchManager: WatchManager @StateObject private var myViewModel: ViewModel init() { let manager = WatchManager() self._watchManager = StateObject(wrappedValue: manager) self._myViewModel = StateObject(wrappedValue: ViewModel(watchManager: manager)) } var body: some View { HStack { Text("Distance: \(watchManager.deltaDistance)") } .padding() HStack { Text("Distance2: \(myViewModel.deltaDistance2)") Button("set DeltaDistance2") { myViewModel.setWithOtherDistance() } // 新增测试按钮:触发WatchManager更新 Button("Update Watch Data") { watchManager.setDistance() } } .padding() } } extension RecordView { @MainActor class ViewModel: ObservableObject { @ObservedObject var watchManager: WatchManager @Published var deltaDistance2 = 0.0 init(watchManager: WatchManager) { self.watchManager = watchManager self.deltaDistance2 = watchManager.deltaDistance // 用Async/Await订阅更新 Task { for await _ in watchManager.$deltaDistance.values { self.deltaDistance2 = watchManager.deltaDistance } } } func setWithOtherDistance() { deltaDistance2 = watchManager.deltaDistance } } } class WatchManager: ObservableObject { @Published var deltaDistance = 0.0 func setDistance() { self.deltaDistance = 9.0 } }
关键说明
- 必须保证
WatchManager实例唯一,ViewModel和视图使用同一个对象,才能实现数据同步。 - 通过订阅
@Published的数据流,可实现deltaDistance更新时自动同步deltaDistance2,无需手动触发。
内容的提问来源于stack exchange,提问作者C Fuchs
相关产品推荐
相关产品推荐

