我有一个视图模型,它是其他子视图模型的父视图模型。即:
public class ViewModel: ObservableObject {
@Published var nav = NavigationViewModel()
@Published var screen = ScreenViewModel()
其他儿童视图模型,如导航和屏幕,都有特定的用途。例如,导航的职责是跟踪当前屏幕:
class NavigationViewModel: ObservableObject {
// MARK: Publishers
@Published var currentScreen: Screen = .Timeline
}
ViewModel在应用程序结构中实例化:
@main
struct Appy_WeatherApp: App {
// MARK: Global
var viewModel = ViewModel()
// MARK: -
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(viewModel)
}
}
}
我在任何需要访问它的视图上为它声明一个@EnvironmentObject:
@EnvironmentObject var viewModel: ViewModel
任何引用ViewModel的非对象属性(该属性为@Published
,其值发生更改(的视图都将导致视图按预期重新渲染。但是,例如,如果NavigationViewModel
的currentScreen@Published
属性发生更改,则不会重新渲染视图。
我知道,如果我将NavigationViewModel
从ViewModel中分离出来,在应用程序级别实例化它,并在访问其任何已发布属性的任何视图中将它用作自己的环境对象,我就能使它发挥作用。
我的问题是,上述解决方法是否真的是处理此问题的正确方法,和/或是否有任何方法可以让视图订阅环境对象的子对象内属性的值更改?或者还有另一种我没有考虑过的方法,那就是我试图通过视图模型职责的碎片化来实现的推荐方法吗?
有几种方法可以实现这一点。
选项1
使用CCD_ 5。
import Combine
public class ViewModel: ObservableObject {
@Published var nav = NavigationViewModel()
var anyCancellable: AnyCancellable?
init() {
anyCancellable = nav.objectWillChange.sink { _ in
self.objectWillChange.send()
}
}
}
基本上,只要navigationViewModel
发布更改,您就可以监听。如果是这样的话,您可以告诉您的观点,您的ViewModel
也发生了变化。
选项2
我想,由于名称NavigationViewModel
,您会经常在其他视图模型中使用它吗?
如果是这样的话,我会选择singleton模式,比如:
class NavigationViewModel: ObservableObject {
static let shared = NavigationViewModel()
private init() {}
@Published var currentScreen: Screen = .Timeline
}
在您的ViewModel
:内部
public class ViewModel: ObservableObject {
var nav: NavigationViewModel { NavigationViewModel.shared }
}
当然,您也可以在任何View
:中调用它
struct ContentView: View {
@StateObject var navigationModel = NavigationModel.shared
}
更改发布者后,您可能需要致电objectWillChange.send()
。
@Published var currentScreen: Screen = .Timeline {
didSet {
objectWillChange.send()
}
}