简体   繁体   English

从视图模型中更新 EnvironmentObject

[英]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.在也将使用该对象的所有视图中,您使用@O​​bservedObject。

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.

相关问题 SwiftUI 视图未更新为 EnvironmentObject 更改 - SwiftUI view is not updating to EnvironmentObject change SwiftUI - 如何将 EnvironmentObject 传递到视图模型中? - SwiftUI - How to pass EnvironmentObject into View Model? 从视图外部设置 SwiftUI @EnvironmentObject - Setting a SwiftUI @EnvironmentObject from outside a view 如何将 EnvironmentObject 传递给该 EnvironmentObject 中的 ObservedObject? - How to pass an EnvironmentObject to an ObservedObject within that EnvironmentObject? 将内容模型转换为 EnvironmentObject - Convert Contentful Model to EnvironmentObject 使用ReactiveCocoa从视图模型更新UITableViewCell - Updating a UITableViewCell from a view model with ReactiveCocoa SwiftUI EnvironmentObject 与 View 不同步 - SwiftUI EnvironmentObject out of sync with View 将 .environmentObject 与作为工作表呈现的视图一起使用会导致 onReceive 不与 @EnvironmentObject 触发/冲突 - Using .environmentObject with a View presented as a sheet causes onReceive not to fire/conflicts with @EnvironmentObject 在 SwiftUI 中通过摇动手势(使用 UIKit)设置 EnvironmentObject 不会更新视图 - Setting EnvironmentObject from a Shake Gesture (using UIKit) in SwiftUI doesn't update View 从 SwiftUI 中的 ViewModel 访问 EnvironmentObject - Access EnvironmentObject from ViewModel in SwiftUI
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM