在 SwiftUI 中,使用单例模式的情况下,即使类遵循 ObservableObject 以及参数的属性包装器为 @Published,如果视图中没有使用 @StateObject 或 @ObservedObject 来观察它,视图就不会更新。
class TemporaryData: ObservableObject {
static var shared = TemporaryData()
private init() {}
@Published var completeCompression = false // 完成压缩,false为未完成,true为完成
@Published var selectedView:SelectedView = .compression
}
在SwiftUI中,尝试修改单例模式 TemporaryData.shared 的属性:
struct ContentView: View {
@State private var selectedView = TemporaryData.shared.selectedView
var body: some View {
Button("修改单例属性"){
selectedView = .compression
}
发现在当前视图中修改成功,当前视图的参数可以根据 selectedView变化发生改变,但实际上并没有修改TemporaryData.shared的属性。
问题原因
这是因为,当我在ContentView中声明:
@State private var selectedView = TemporaryData.shared.selectedView
这意味着创建了一个与 TemporaryData.shared 无关的本地副本,修改的是本地的 selectedView,而不是 TemporaryData.shared 的 selectedView。
解决方案
如果要监听并修改TemporaryData.shared中的属性,可以使用以下两种方案:
方案1:使用@ObservedObject
struct ContentView: View {
@ObservedObject private var selectedView = TemporaryData.shared.selectedView
var body: some View {
Button("修改单例属性"){
selectedView = .compression
}
将 @State改为 @ObservedObject。
@ObservedObject 要求对象符合ObservableObject协议,所以不能使用属性,否则就会报错:
Generic struct 'ObservedObject' requires that 'SelectedView' conform to 'ObservableObject'
因此,需要将TemporaryData.shared.selectedView属性修改为TemporaryData.shared单例。
@State private var selectedView = TemporaryData.shared.selectedView // 修改前
@ObservedObject private var selectedView = TemporaryData.shared // 修改后
这样就可以通过修改 selectedView 实现修改 TemporaryData.shared 的目的。
方案2:使用 @EnvironmentObject
在入口文件注入环境对象:
// App 启动时注入环境对象
@main
struct ImageSlimApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(TemporaryData.shared)
}
}
}
在视图中使用 @EnvironmentObject引入环境对象:
struct ContentView: View {
@EnvironmentObject var selectedView = TemporaryData
var body: some View {
Button("修改单例属性"){
selectedView = .compression
}
这两个方案都可以解决修改单例模式属性,从而实现所有视图的监听和数据共享。
总结
在SwiftUI中使用单例模式,如果想要在其他视图中访问,可以直接获取单例数据。例如:
Text("\(TemporaryData.shared.selectedView)")
但是,如果需要修改单例模式的数据,就需要使用 @ObservedObject 或 @EnvironmentObject,通过观察对象或环境对象获取单例模式,从而实现对于单例模式的修改。
扩展知识
@StateObject可以观察单例模式么?
如果使用 @StateObject,实际上也可以观察到单例属性:
struct ContentView: View {
@StateObject var selectedView = TemporaryData.shared
var body: some View {
Button("修改单例属性"){
selectedView = .compression
}
@StateObject适用于首次创建该对象的视图,当前视图(自己)管理,如果希望SwiftUI创建并管理对象,可以使用 @StateObject。
@ObservedObject适用于外部传入的 ObservableObject,生命周期由父视图或其他外部管理,例如父视图已经创建好,只是子视图拿来观察。
所以,当使用 @StateObject 观察单例模式时:
@StateObject var data = TemporaryData.shared
这表示当前视图拥有并管理这个对象的生命周期,SwiftUI只会在视图初始化时创建一次对象,而对象重建时,对象并不会重新创建。
而 TemporaryData.shared 实际上是已经存在的,所以 @StateObject 的管理创建在这里没有意义。
相比之下,ObservedObject则用于观察外部对象,比如父视图传入的 model,或像这种单例共享对象。
总的来说,@StateObject用于首次创建并管理的对象,@ObservedObject用于外部传入的对象,所以单例模式更适合后者。
相关文章
Swift控制全局的单例模式:https://fangjunyu.com/2024/10/19/swift%e6%8e%a7%e5%88%b6%e5%85%a8%e5%b1%80%e7%9a%84%e5%8d%95%e4%be%8b%e6%a8%a1%e5%bc%8f/