订阅环境视图模型的子对象内的值更改(发生这种情况时视图不会被重新渲染)
Subscribing to value changes inside child objects of environment view models (view not being re-rendered when this happens)
我有一个视图模型是其他子视图模型的父视图模型。即:
public class ViewModel: ObservableObject {
@Published var nav = NavigationViewModel()
@Published var screen = ScreenViewModel()
其他子视图模型,如导航和屏幕,都有特定的用途。例如,nav 的职责是跟踪当前屏幕:
class NavigationViewModel: ObservableObject {
// MARK: Publishers
@Published var currentScreen: Screen = .Timeline
}
ViewModel 在 App 结构中实例化:
@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 分开,我可以让它工作,在应用程序级别实例化它,并在访问其任何已发布属性的任何视图中将其用作自己的环境对象。
我的问题是上述解决方法是否真的是处理此问题的正确方法,and/or 是否有任何方法可以让视图订阅环境对象的子对象内属性的值更改?还是有另一种我没有考虑过的方法,这是我试图通过视图模型职责的碎片化来实现的推荐方法?
有几种方法可以实现这一点。
选项 1
使用 Combine
.
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
,您会经常在其他视图模型中使用它吗?
如果是这样,我会选择单例模式,如下所示:
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()
}
}
我有一个视图模型是其他子视图模型的父视图模型。即:
public class ViewModel: ObservableObject {
@Published var nav = NavigationViewModel()
@Published var screen = ScreenViewModel()
其他子视图模型,如导航和屏幕,都有特定的用途。例如,nav 的职责是跟踪当前屏幕:
class NavigationViewModel: ObservableObject {
// MARK: Publishers
@Published var currentScreen: Screen = .Timeline
}
ViewModel 在 App 结构中实例化:
@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 分开,我可以让它工作,在应用程序级别实例化它,并在访问其任何已发布属性的任何视图中将其用作自己的环境对象。
我的问题是上述解决方法是否真的是处理此问题的正确方法,and/or 是否有任何方法可以让视图订阅环境对象的子对象内属性的值更改?还是有另一种我没有考虑过的方法,这是我试图通过视图模型职责的碎片化来实现的推荐方法?
有几种方法可以实现这一点。
选项 1
使用 Combine
.
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
,您会经常在其他视图模型中使用它吗?
如果是这样,我会选择单例模式,如下所示:
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()
}
}