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

Swift MVVM架构下如何让视图监听结构体类型ViewModel的变化

问题核心原因

  • 你的ViewModel是值类型,num是存储属性,只会在初始化时赋值一次,后续Model变更不会自动更新这个属性
  • SwiftUI的View只有在自身状态发生变化时才会重绘,当前你没有让View监听Model的变更通知,所以View不会刷新,也不会生成新的ViewModel实例

修正方案

方案1:保留结构体ViewModel,依赖View重绘时自动更新

修改后的完整可运行代码如下:

import SwiftUI

class Model: ObservableObject {
    var nums: [Int]
    
    init() {
        self.nums = Array(1..<100)
    }
    
    func getNum() -> Int {
        return nums.count
    }
    
    func add() {
        nums.append(nums.count + 1)
        self.objectWillChange.send()
    }
}

struct ViewModel {
    var model: Model
    // 改成计算属性,每次访问都会从Model取最新值,不需要固化存储
    var num: Int {
        model.getNum()
    }
    
    init(model: Model) {
        self.model = model
    }
    
    func trigger() {
        model.add()
        print("Triggered")
    }
}

struct ContentView: View {
    // 用@StateObject持有ObservableObject的Model,自动监听变更
    @StateObject var model: Model
    // View每次重绘都会重新生成ViewModel,拿到最新状态
    var viewModel: ViewModel {
        ViewModel(model: model)
    }
    
    var body: some View {
        Button(action: { viewModel.trigger() }) {
            Text("Press")
        }
        Text("Number of Elements")
        Text("\(viewModel.num)")
    }
}

@main
struct app: App {
    // 全局Model实例在根节点用@StateObject初始化
    @StateObject private var model = Model()
    var body: some Scene {
        WindowGroup {
            ContentView(model: model)
        }
    }
}

方案说明

  • 用@StateObject持有Model实例后,Model发送objectWillChange通知时,当前View会自动触发重绘
  • 将ViewModel改成View的计算属性,每次View重绘时都会生成新的ViewModel实例,自动同步Model的最新状态
  • 把ViewModel的num改成计算属性可以避免存储属性初始化后无法更新的问题,你也可以选择保留存储属性,因为每次重绘都会生成新的ViewModel,存储属性也会重新计算赋值,两种写法都可以正常运行

可选优化

如果你的ViewModel需要缓存大量不常变的属性,避免每次重绘都重新生成全部属性,可以给ViewModel加上缓存逻辑,或者将需要响应变更的属性单独抽成计算属性即可。

内容的提问来源于stack exchange,提问作者John Sorensen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 02:27:01