如何从视图模型类中修改模型数据?
嘿,我看你现在遇到的问题是在DetailViewModel里修改MyModel的数据,但ContentView里的模型没同步更新对吧?这其实是因为Swift里的struct是值类型,你把model传给DetailViewModel的时候,相当于传了一份副本,修改副本自然不会影响原数据。下面给你两种常用的解决办法:
方法一:把模型改成引用类型(Class)
如果你的模型需要在多个视图或视图模型之间共享状态,把MyModel从struct改成class是最直接的方案。因为类是引用类型,所有持有它的对象操作的都是同一个实例,修改会自动同步。
首先调整MyModel,让它遵循ObservableObject,并用@Published标记需要视图响应的属性:
import SwiftUI class MyModel: ObservableObject { @Published var pages: [Int] = [] }
然后修改ContentView,因为现在是引用类型,要用@StateObject来管理模型实例(@State是给值类型用的):
struct ContentView: View { @StateObject private var model = MyModel() var body: some View { NavigationStack { VStack { Text("Content View") // 加个文本显示pages,方便实时查看变化 Text("Pages count: \(model.pages.count)") NavigationLink("Details") { DetailView(viewModel: DetailViewModel(model: model)) } } .onAppear { print(model.pages) } } } }
接下来是DetailViewModel,直接持有MyModel的引用,修改时操作这个引用即可:
class DetailViewModel: ObservableObject { private let model: MyModel init(model: MyModel) { self.model = model } // 示例:添加一个修改pages的方法 func addPage() { model.pages.append(model.pages.count + 1) } }
最后调整DetailView,因为DetailViewModel现在是引用类型,建议用@StateObject来管理:
struct DetailView: View { @StateObject private var viewModel: DetailViewModel init(viewModel: DetailViewModel) { _viewModel = StateObject(wrappedValue: viewModel) } var body: some View { VStack { Text("Detail View") Button("Add Page") { viewModel.addPage() } } } }
这样点击DetailView里的按钮,model.pages的变化会同步回ContentView,视图也会自动刷新。
方法二:使用Binding(适合简单父子视图传递)
如果你不想把模型改成类,也可以用SwiftUI的Binding来让视图模型间接修改原数据。这种方式适合仅在父子视图之间传递修改的场景。
首先保持MyModel为struct不变,修改DetailViewModel,让它持有Binding<MyModel>:
class DetailViewModel: ObservableObject { private let modelBinding: Binding<MyModel> // 通过binding获取当前模型数据 var model: MyModel { modelBinding.wrappedValue } init(modelBinding: Binding<MyModel>) { self.modelBinding = modelBinding } // 示例:修改pages的方法 func addPage() { modelBinding.wrappedValue.pages.append(model.pages.count + 1) } }
然后调整ContentView里的NavigationLink,把model的Binding传给DetailView:
NavigationLink("Details") { DetailView(viewModel: DetailViewModel(modelBinding: $model)) }
最后修改DetailView的初始化(现在不需要@autoclosure了,直接传ViewModel实例即可):
struct DetailView: View { @StateObject private var viewModel: DetailViewModel init(viewModel: DetailViewModel) { _viewModel = StateObject(wrappedValue: viewModel) } var body: some View { VStack { Text("Detail View") Button("Add Page") { viewModel.addPage() } } } }
这样点击按钮时,通过Binding修改的是ContentView里的原模型数据,同样能实现同步更新。
两种方案各有优劣:如果模型状态需要跨多个组件共享,推荐用Class+ObservableObject;如果只是简单的父子视图交互,Binding更轻量。你可以根据自己的需求选择~
备注:内容来源于stack exchange,提问作者JAHelia

