如何使不可观察类的方法可用于 SwiftUI 视图?



我有一个非常复杂的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))
        }
    }
}

最新更新