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

SwiftUI中为何子视图ViewModel属性的onChange监听不生效?

问题原因

  • ContentViewVM里的@Published var childVM只会在childVM这个实例本身被替换(比如重新赋值一个新的ChildVM对象)时,才会通知ContentView更新视图。但childVM内部的isPaymentPresented属性变化时,ContentViewVM并不会触发任何变更通知,所以ContentView的.onChange监听不到这个深层变化。
  • ChildView里用@StateObject声明传入的viewModel是错误的——@StateObject用于让视图自己创建并持有ViewModel实例,而这里的viewModel是从父视图传过来的,应该用@ObservedObject来观察外部对象的变化。

解决方案

方案1:直接订阅子ViewModel的属性发布者

用.onReceive直接监听childVM.isPaymentPresented的变化,无需经过父ViewModel转发:

class ContentViewVM: ObservableObject {
    @Published var childVM = ChildVM()
}

struct ContentView: View {
    @StateObject var contentVM = ContentViewVM()
    
    var body: some View {
        ChildView(viewModel: contentVM.childVM)
            .onReceive(contentVM.childVM.$isPaymentPresented) { newValue in
                print("Change Observed: \(newValue)")
            }
    }
}

struct ChildView: View {
    @ObservedObject var viewModel: ChildVM
    var body: some View {
        Button("Deep Level Trigger") {
            viewModel.isPaymentPresented.toggle()
            print("Deep Level Triggered: \(viewModel.isPaymentPresented)")
        }
    }
}

class ChildVM: ObservableObject {
    @Published var isPaymentPresented: Bool = false
}

方案2:在父ViewModel中转发子属性的变化

通过Combine让ContentViewVM监听childVM的属性变化,并同步到自身的@Published属性,这样ContentView就能用原来的.onChange监听:

import Combine

class ContentViewVM: ObservableObject {
    @Published var childVM = ChildVM()
    @Published var isPaymentPresented = false
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        childVM.$isPaymentPresented
            .assign(to: \.isPaymentPresented, on: self)
            .store(in: &cancellables)
    }
}

struct ContentView: View {
    @StateObject var contentVM = ContentViewVM()
    
    var body: some View {
        ChildView(viewModel: contentVM.childVM)
            .onChange(of: contentVM.isPaymentPresented) { newValue in
                print("Change Observed: \(newValue)")
            }
    }
}

struct ChildView: View {
    @ObservedObject var viewModel: ChildVM
    var body: some View {
        Button("Deep Level Trigger") {
            viewModel.isPaymentPresented.toggle()
            print("Deep Level Triggered: \(viewModel.isPaymentPresented)")
        }
    }
}

class ChildVM: ObservableObject {
    @Published var isPaymentPresented: Bool = false
}

内容的提问来源于stack exchange,提问作者Aaban Tariq Murtaza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 01:53:14