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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:30:52