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

SwiftUI中更新ViewModel内Model属性后如何同步更新视图?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 08:34:42