SwiftUI中如何观察嵌套对象属性值?多层场景实现疑问
解决SwiftUI嵌套ObservableObject属性更新不刷新UI的问题
你的核心问题是:SampleModel中@Published修饰的devicesToUpdated数组,仅会在数组本身(元素增减、整体替换)变化时触发UI刷新,但数组内部ScanedDevice对象的currentStatusText属性更新时,不会触发SampleModel的变更通知,导致UI无法实时更新。
下面是几种可行的解决方案:
方案一:让ScanedDevice成为ObservableObject,手动转发变更事件
首先修改ScanedDevice,让它本身成为可观察对象,并用@Published修饰需要监听的属性:
class ScanedDevice: NSObject, Identifiable, ObservableObject { let id = UUID() // 实现Identifiable的必要属性 @Published var currentStatusText: String = "Pending" // 用于存储订阅对象,避免提前释放 private var subscription: AnyCancellable? func cancelSubscription() { subscription?.cancel() } }
然后修改SampleModel,在数组变化时,监听每个设备的变更事件并转发给自己的objectWillChange:
class SampleModel: ObservableObject { @Published var devicesToUpdated: [ScanedDevice] = [] { didSet { // 移除旧设备的监听 oldValue.forEach { $0.cancelSubscription() } // 给新设备添加监听,设备变更时触发Model的变更通知 devicesToUpdated.forEach { device in let cancellable = device.objectWillChange.sink { [weak self] _ in self?.objectWillChange.send() } device.subscription = cancellable } } } }
方案二:用Combine合并所有设备的变更事件(更简洁)
同样先让ScanedDevice成为ObservableObject:
class ScanedDevice: NSObject, Identifiable, ObservableObject { let id = UUID() @Published var currentStatusText: String = "Pending" }
然后在SampleModel中通过Combine的merge和switchToLatest,自动监听所有设备的变更事件:
class SampleModel: ObservableObject { @Published var devicesToUpdated: [ScanedDevice] = [] private var cancellables = Set<AnyCancellable>() init() { $devicesToUpdated // 每次数组变化,合并所有设备的objectWillChange事件流 .map { devices in devices.map { $0.objectWillChange } .merge() } .switchToLatest() // 任何设备变更时,触发Model的变更通知 .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) } }
这个方案无需给ScanedDevice额外添加订阅存储属性,代码更简洁,是更推荐的实现方式。
方案三:手动通知(耦合性高,不推荐)
如果不想使用Combine,也可以通过通知中心手动触发变更,但这种方式会增加代码耦合性,仅作备选:
class ScanedDevice: NSObject, Identifiable { let id = UUID() var currentStatusText: String = "Pending" { didSet { NotificationCenter.default.post(name: NSNotification.Name("DeviceStatusUpdated"), object: self) } } } class SampleModel: ObservableObject { @Published var devicesToUpdated: [ScanedDevice] = [] private var cancellables = Set<AnyCancellable>() init() { NotificationCenter.default.publisher(for: NSNotification.Name("DeviceStatusUpdated")) .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) } }
以上三种方案都能实现currentStatusText更新时UI实时刷新,优先选择方案二,更符合SwiftUI和Combine的设计理念。
内容的提问来源于stack exchange,提问作者Ron Weasley
相关产品推荐
相关产品推荐

