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
相关产品推荐
相关产品推荐

