如何观察用户默认值的变化?



我的观点中有一个@ObservedObject

struct HomeView: View {
@ObservedObject var station = Station()
var body: some View {
Text(self.station.status)
}

它根据来自Station.statusString更新文本:

class Station: ObservableObject {
@Published var status: String = UserDefaults.standard.string(forKey: "status") ?? "OFFLINE" {
didSet {
UserDefaults.standard.set(status, forKey: "status")
}
}      

但是,我需要更改AppDelegatestatus的值,因为这是我接收Firebase云消息的地方:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// Print full message.
let rawType = userInfo["type"]
// CHANGE VALUE OF status HERE
}

但是,如果我在AppDelegate中更改statusUserDefaults值 - 它不会在我的视图中更新。

status发生变化时,如何通知我视图中的@ObservedObject

编辑:忘了提到在上述示例中使用了SwiftUI的2.0测试版。

这是可能的解决方案

import Combine
// define key for observing
extension UserDefaults {
@objc dynamic var status: String {
get { string(forKey: "status") ?? "OFFLINE" }
set { setValue(newValue, forKey: "status") }
}
}
class Station: ObservableObject {
@Published var status: String = UserDefaults.standard.status {
didSet {
UserDefaults.standard.status = status
}
}
private var cancelable: AnyCancellable?
init() {
cancelable = UserDefaults.standard.publisher(for: .status)
.sink(receiveValue: { [weak self] newValue in
guard let self = self else { return }
if newValue != self.status { // avoid cycling !!
self.status = newValue
}
})
}
}

注意:SwiftUI 2.0允许您直接通过AppStorage在视图中使用/观察UserDefaults,因此如果您只需要在视图中使用该状态,则可以使用

struct SomeView: View {
@AppStorage("status") var status: String = "OFFLINE"
...

如果需要,我建议您使用环境对象或两者的组合。环境对象基本上是一个全局状态对象。因此,如果您更改环境对象的已发布属性,它将反映您的视图。要进行设置,您需要通过SceneDelegate将对象传递到初始视图,并且可以在整个视图层次结构中使用状态。这也是在非常远的同级视图之间传递数据的方法(或者如果您有更复杂的方案(。

简单示例

在场景委托中.swift:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

let contentView = ContentView().environmentObject(GlobalState())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}

全局状态应符合ObservableObject。你应该把你的全局变量放在那里作为@Published

class GlobalState: ObservableObject {
@Published var isLoggedIn: Bool

init(isLoggedIn : Bool) {
self.isLoggedIn = isLoggedIn
}
}

如何发布变量的示例,与 SceneDelegate 中已显示的示例无关

然后,这就是如何在视图中使用全局状态的方法。您需要像这样使用@EnvironmentObject包装器注入它:

struct ContentView: View {

@EnvironmentObject var globalState: GlobalState
var body: some View {
Text("Hello World")
}
}

现在,在您的情况下,您还希望使用AppDelegate中的状态。为此,我建议您在 AppDelegate 中保护全局状态变量,并在传递到初始视图之前从 SceneDelegate 中访问它。要实现此目的,您应该在应用程序委托中添加以下内容:

var globalState : GlobalState!

static func shared() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}

现在,您可以返回到 SceneDelegate 并执行以下操作,而不是直接初始化 GlobalState:

let contentView = ContentView().environmentObject(AppDelegate.shared().globalState)

最新更新