SwiftUI中更新ViewModel内Model属性后如何同步更新视图?
嗨,我来帮你搞定这个问题~你现在遇到的核心问题是:你的MyViewModel里用@Published修饰了model对象,但@Published只会在整个model对象被替换的时候才会触发视图更新,而你只是修改了model内部的title属性,ViewModel并没有感知到这个变化,所以视图自然不会刷新。
下面给你几种可行的解决方案,你可以根据自己的需求选择:
方案一:让ViewModel监听Model的内部变化(最推荐)
利用Combine框架,让ViewModel订阅Model的objectWillChange事件,当Model内部属性变化时,主动触发ViewModel的更新通知。代码修改如下:
import Combine // 别忘了导入Combine class MyModel: ObservableObject { @Published var title: String = "" init(title: String) { self.title = title } } class MyViewModel: ObservableObject { let model = MyModel(title: "Apple") private var cancellables = Set<AnyCancellable>() init() { // 订阅Model的变化,一旦Model内部属性改变,就触发ViewModel的更新 model.objectWillChange .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) } func updateToBanana() { model.title = "Banana" } } struct MyView: View { @StateObject var viewModel = MyViewModel() var body: some View { VStack(spacing: 20) { Text(viewModel.model.title) Button { viewModel.updateToBanana() } label: { Text("Banana") } } } }
这种方法的好处是:不管你后续给Model加多少属性,只要用@Published修饰,ViewModel都会自动感知变化,不需要重复修改代码。
方案二:在ViewModel里代理Model的属性
把Model的属性“搬到”ViewModel里,用@Published修饰,再同步到Model中。这样视图直接绑定ViewModel的属性,就能自动刷新了:
class MyViewModel: ObservableObject { private let model = MyModel(title: "Apple") // 代理Model的title属性 @Published var title: String { didSet { model.title = title } } init() { // 初始化时同步Model的初始值 title = model.title } func updateToBanana() { title = "Banana" } } struct MyView: View { @StateObject var viewModel = MyViewModel() var body: some View { VStack(spacing: 20) { // 直接绑定ViewModel的title Text(viewModel.title) Button { viewModel.updateToBanana() } label: { Text("Banana") } } } }
这种方法适合Model属性不多的场景,代码更直观,视图层不需要直接接触Model。
方案三:手动触发ViewModel的更新(应急用)
如果只是临时解决某个特定场景,可以在修改Model属性后,手动发送ViewModel的更新通知:
class MyViewModel: ObservableObject { @Published var model = MyModel(title: "Apple") func updateToBanana() { // 先发送更新通知,再修改属性 objectWillChange.send() model.title = "Banana" } }
不过这种方法不推荐长期使用,因为每次修改Model的属性都要手动加这一行,容易遗漏,代码也不够优雅。
另外你提到把@Published改成@ObservedObject没用,是因为@ObservedObject需要在View层级里使用才能触发刷新,而在ViewModel(ObservableObject子类)里,它不会自动把内部ObservedObject的变化传递给ViewModel的更新通知,所以才没效果~
备注:内容来源于stack exchange,提问作者supasit suebsunthorn

