SwiftUI中@Published属性更新未在UI中生效的问题排查
问题原因
你的SimpleViewModel用@Published修饰了predictor,但@Published只会在对象本身被替换时发送视图更新通知。而SimplePredictor内部的@Published var label变化时,predictor对象本身并没有被替换,所以SimpleViewModel不会触发objectWillChange通知,导致SwiftUI视图没有重新计算body,Text自然不会更新显示新值。
虽然onReceive能订阅到predictor.$label的变化(这是直接监听SimplePredictor内部的发布者),但这个回调不会触发视图整体刷新,Text还是会使用旧的body计算结果。
解决方法
方法1:让ViewModel转发Predictor的更新通知
修改SimpleViewModel,订阅predictor的更新事件,当predictor内部有变化时,手动发送ViewModel自己的更新通知:
import SwiftUI import Combine // 需要导入Combine框架 class SimplePredictor: ObservableObject { @Published var label: String = "N/A" func updateLabel() { DispatchQueue.main.asyncAfter(deadline: .now() + 2) { self.label = "Updated Label" print("Label set to: \(self.label)") } } } class SimpleViewModel: ObservableObject { let predictor = SimplePredictor() // 此处不再需要@Published private var cancellables = Set<AnyCancellable>() init() { // 订阅predictor的更新事件,转发给ViewModel的objectWillChange predictor.objectWillChange .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) } } struct SimpleContentView: View { @StateObject var viewModel = SimpleViewModel() var body: some View { VStack { Text(viewModel.predictor.label) .onReceive(viewModel.predictor.$label) { newLabel in print("Label received: \(newLabel)") } } .padding() .onAppear { viewModel.predictor.updateLabel() } } }
方法2:将Label属性提升到ViewModel中
如果业务逻辑允许,直接让ViewModel持有label属性,避免嵌套ObservableObject的监听问题:
class SimplePredictor { func updateLabel(completion: @escaping (String) -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + 2) { let newLabel = "Updated Label" print("Label set to: \(newLabel)") completion(newLabel) } } } class SimpleViewModel: ObservableObject { @Published var label: String = "N/A" private let predictor = SimplePredictor() func updateLabel() { predictor.updateLabel { [weak self] newLabel in self?.label = newLabel } } } struct SimpleContentView: View { @StateObject var viewModel = SimpleViewModel() var body: some View { VStack { Text(viewModel.label) .onReceive(viewModel.$label) { newLabel in print("Label received: \(newLabel)") } } .padding() .onAppear { viewModel.updateLabel() } } }
方法3:让视图直接观察Predictor
在视图中直接将predictor声明为@ObservedObject,绕过ViewModel的嵌套监听:
struct SimpleContentView: View { @StateObject var viewModel = SimpleViewModel() @ObservedObject var predictor: SimplePredictor init() { let vm = SimpleViewModel() self.predictor = vm.predictor self._viewModel = StateObject(wrappedValue: vm) } var body: some View { VStack { Text(predictor.label) .onReceive(predictor.$label) { newLabel in print("Label received: \(newLabel)") } } .padding() .onAppear { predictor.updateLabel() } } }
内容的提问来源于stack exchange,提问作者Megool
相关产品推荐
相关产品推荐

