[英]How to observe changes in UserDefaults?
我的視圖中有一個@ObservedObject
:
struct HomeView: View {
@ObservedObject var station = Station()
var body: some View {
Text(self.station.status)
}
它根據來自Station.status
的String
更新文本:
class Station: ObservableObject {
@Published var status: String = UserDefaults.standard.string(forKey: "status") ?? "OFFLINE" {
didSet {
UserDefaults.standard.set(status, forKey: "status")
}
}
但是,我需要在AppDelegate
中更改status
的值,因為這是我收到Firebase Cloud Messages的地方:
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
中的status
UserDefaults
值 - 在我看來它不會更新。
當status
更改時,如何通知我視圖中的@ObservedObject
?
編輯:忘記提及在上述示例中使用了 SwiftUI 的 2.0 beta 版本。
這是可能的解決方案
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"
...
如果需要,我建議您使用環境 object或兩者的組合。 環境對象基本上是一個全局 state對象。 因此,如果您更改環境 object 的已發布屬性,它將反映您的視圖。 要設置它,您需要通過SceneDelegate將 object 傳遞給您的初始視圖,並且您可以在整個視圖層次結構中使用 state。 這也是跨非常遠的兄弟視圖(或者如果您有更復雜的場景)傳遞數據的方式。
在您的 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()
}
}
全局 state 應符合ObservableObject 。 您應該將全局變量作為@Published放在那里。
class GlobalState: ObservableObject {
@Published var isLoggedIn: Bool
init(isLoggedIn : Bool) {
self.isLoggedIn = isLoggedIn
}
}
如何發布變量的示例,與 SceneDelegate 中已顯示的示例無關
這就是您如何在視圖中使用全局 state 的方式。 您需要使用@EnvironmentObject包裝器注入它,如下所示:
struct ContentView: View {
@EnvironmentObject var globalState: GlobalState
var body: some View {
Text("Hello World")
}
}
現在,在您的情況下,您還想在 AppDelegate 中使用 state。 為了做到這一點,我建議您在 AppDelegate 中保護全局 state 變量,並在您的 SceneDelegate 中從那里訪問它,然后再傳遞到初始視圖。 為此,您應該在 AppDelegate 中添加以下內容:
var globalState : GlobalState!
static func shared() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
現在您可以 go 回到您的 SceneDelegate 並執行以下操作,而不是直接初始化 GlobalState:
let contentView = ContentView().environmentObject(AppDelegate.shared().globalState)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.