[英]Updating EnvironmentObject from within the View Model
In SwiftUI, I want to pass an environment object to a view model so I can change/update it.在 SwiftUI 中,我想将环境对象传递给视图模型,以便我可以更改/更新它。 The EnvironmentObject is a simple AppState which consists of a single property counter. EnvironmentObject 是一个简单的 AppState,它由一个属性计数器组成。
class AppState: ObservableObject {
@Published var counter: Int = 0
}
The view model "CounterViewModel" updates the environment object as shown below:视图模型“CounterViewModel”更新环境对象,如下所示:
class CounterViewModel: ObservableObject {
var appState: AppState
init(appState: AppState) {
self.appState = appState
}
var counter: Int {
appState.counter
}
func increment() {
appState.counter += 1
}
}
The ContentView displays the value: ContentView 显示值:
struct ContentView: View {
@ObservedObject var counterVM: CounterViewModel
init(counterVM: CounterViewModel) {
self.counterVM = counterVM
}
var body: some View {
VStack {
Text("\(counterVM.counter)")
Button("Increment") {
counterVM.increment()
}
}
}
}
I am also injecting the state as shown below:我也在注入状态,如下所示:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
let appState = AppState()
ContentView(counterVM: CounterViewModel(appState: appState))
.environmentObject(appState)
}
}
}
The problem is that when I click the increment button, the counterVM.counter never returns the updated value.问题是当我单击增量按钮时,counterVM.counter 永远不会返回更新的值。 What am I missing?我错过了什么?
I'm not sure why both the CounterViewModel and the AppState need to be observable objects, since you are using a view model to format the content of your models.我不确定为什么 CounterViewModel 和 AppState 都需要是可观察的对象,因为您正在使用视图模型来格式化模型的内容。 I would consider AppState to be a model and I could therefore define it as a struct.我会认为 AppState 是一个模型,因此我可以将它定义为一个结构。 The CounterViewModel will then be the ObservableObject and it published the AppState. CounterViewModel 将成为 ObservableObject 并发布 AppState。 In this way your code is clean and works.通过这种方式,您的代码是干净且有效的。
Code for AppState: AppState 的代码:
import Foundation
struct AppState {
var counter: Int = 0
}
Code for CounterViewModel: CounterViewModel 的代码:
import SwiftUI
class CounterViewModel: ObservableObject {
@Published var appState: AppState
init(appState: AppState) {
self.appState = appState
}
var counter: Int {
appState.counter
}
func increment() {
appState.counter += 1
}
}
Code for the ContentView: import SwiftUI ContentView 的代码:导入 SwiftUI
struct ContentView: View {结构内容视图:查看{
@StateObject var counterVM = CounterViewModel(appState: AppState())
var body: some View {
VStack {
Text("\(counterVM.counter)")
Button("Increment") {
counterVM.increment()
}
}
}
} }
Do remind, that in the View where you first define an ObservableObject, you define it with @StateObject.请注意,在您首先定义 ObservableObject 的 View 中,您使用 @StateObject 定义它。 In all the views that will also use that object, you use @ObservedObject.在也将使用该对象的所有视图中,您使用@ObservedObject。
This code will work.此代码将起作用。
Kind regards, MacUserT亲切的问候, MacUserT
Your class CounterViewModel
is an ObservableObject, but it has no @Published
properties – so no changes will be published automatically to the views.您的类CounterViewModel
是一个 ObservableObject,但它没有@Published
属性——因此不会自动将更改发布到视图。
But you can manually publish changes by using objectWillChange.send()
:但是您可以使用objectWillChange.send()
手动发布更改:
func increment() {
objectWillChange.send()
appState.counter += 1
}
Did you check your xxxxApp.swift
(used to be the AppDelegate
) file ?你检查过你的xxxxApp.swift
(以前是AppDelegate
)文件吗? Sometimes Xcode would do it for you automatically, sometimes won't you have to add it manually and add your environment object.有时 Xcode 会自动为您完成,有时您不必手动添加它并添加您的环境对象。 * It has to be the view that contains all the view you want to share the object to. * 它必须是包含您要共享对象的所有视图的视图。
var body: some Scene {
WindowGroup {
VStack {
ContentView()
.environmentObject(YourViewModel())
}
}
}
We actually don't use view model objects in SwiftUI for view data.我们实际上不使用 SwiftUI 中的视图模型对象来获取视图数据。 We use an @State
struct and if we need to mutate it in a subview we pass in a binding, eg我们使用@State
结构,如果我们需要在子视图中对其进行变异,我们传入一个绑定,例如
struct Counter {
var counter: Int = 0
mutating func increment() {
counter += 1
}
}
struct ContentView: View {
@State var counter = Counter()
var body: some View {
ContentView2(counter: $counter)
}
}
struct ContentView2: View {
@Binding var counter: Counter // if we don't need to mutate it then just use let and body will still be called when the value changes.
var body: some View {
VStack {
Text(counter.counter, format: .number) // the formatting must be done in body so that SwiftUI will update the label automatically if the region settings change.
Button("Increment") {
counter.increment()
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.