我有一个非常复杂的UI,所以我把我的视图模型分解成多个可观察对象类来管理UI的每个部分。
这两个视图模型实例是在"main"类称为Manager。Manager包含在视图模型中操作已发布属性的方法。
我希望这些方法可以从我的视图中使用,但由于管理器类不符合ObservableObject协议,因为它没有任何已发布的属性,我在哪里创建管理器类的实例,以便我可以从多个视图使用它的方法 ?
重要的次要问题 :根据来自HackingWithSwift的Paul Hudson,他建议用@MainActor属性标记所有的ObservableObjects类。但如果我这样做,Manager类的方法将无法操作视图模型的已发布属性,因为并非所有来自Manager的方法都必须在主队列上运行。解决方案是什么?
import SwiftUI
class ViewModel1: ObservableObject {
@Published var duration = 0.0
}
class ViewModel2: ObservableObject {
@Published var currentTime = 0.0
}
class Manager {
var vm1 = ViewModel1()
var vm2 = ViewModel2()
func play() {
//Code that changes the published properties of the 2 view models
}
func pause() {
//Code that changes the published properties of the 2 view models
}
}
struct ContentView: View {
@EnvironmentObject var manager: Manager //Won't work since Manager doesn't conform to ObservableObject
var body: some View {
//View code
}
}
一种可能的方法是将manager创建为常规属性,但将视图模型作为环境注入,如
struct ContentView: View {
private var manager = Manager()
var body: some View {
SomeRootView()
.environmentObject(manager.vm1)
.environmentObject(manager.vm2)
}
}
或在每个子视图的位置,这取决于在哪个子视图层次结构中需要相应的视图模型。
如果管理器需要在深层子视图的某个地方,那么可以通过注入Environment
值将其转移到那里,就像https://stackoverflow.com/a/61847419/12299030一样。或者使其成为单例并通过Manager.shared
使用。
完整的发现和变体代码
当更新子类的属性时,您需要使用objectWillChange.send
手动触发更改事件。
class ViewModel1: ObservableObject {
@Published var duration = 0.0
}
class Manager: ObservableObject {
var vm1 = ViewModel1()
func play() {
vm1.duration += 10
objectWillChange.send()
}
}
struct ContentView: View {
@ObservedObject var manager = Manager() // init here only for testing
var body: some View {
Button {
manager.play()
} label: {
Text (String(manager.vm1.duration))
}
}
}