[英]Refresh SwiftUI View Based on Local Notification
我有以下代碼:
@State private var isDataImported: Bool = false
init() {
NotificationCenter.default.addObserver(forName: .onDataImported, object: nil, queue: nil) { [self] notification in
print("Data has been imported...")
DispatchQueue.main.async {
self.isDataImported = true
print(self.isDataImported) // prints out false
}
}
}
我可以調試並看到通知正在被觸發。 正在打印“數據已導入”行。 我想更新 self.isDataImported 屬性,然后刷新我的視圖。
if isDataImported {
ShowDataView()
} else {
ProgressView()
}
但是視圖 ShowDataView 永遠不會出現,因為 isDataImported 總是假的。 我錯過了什么?
SwiftUI 視圖的傳遞性質使得嘗試在視圖的init
捕獲對self
的引用之類的操作存在問題。 這里有幾個解決方案:
將所有內容保留在View
:
struct ContentView: View {
@State private var isDataImported: Bool = false
@State private var cancellable : AnyCancellable?
var body: some View {
Group {
if isDataImported {
Text("Has data")
} else {
Text("Does not have data")
}
}.onAppear {
cancellable = NotificationCenter.default.publisher(for: .onDataImported)
.receive(on: RunLoop.main)
.sink { notification in
self.isDataImported = true
}
NotificationCenter.default.post(name: .onDataImported, object: nil)
}
}
}
這通常是我會做的,將其移動到視圖模型中,以便您可以保持onAppear
更干凈一點。 因為視圖模型是一個class
並且具有可靠的、基於引用的生命周期,所以對self
的賦值問題較少:
class ViewModel: ObservableObject {
@Published var isDataImported: Bool = false
private var cancellable : AnyCancellable?
init() {
cancellable = NotificationCenter.default.publisher(for: .onDataImported)
.receive(on: RunLoop.main)
.sink { notification in
self.isDataImported = true
}
}
}
struct ContentView : View {
@StateObject var viewModel = ViewModel()
var body: some View {
Group {
if viewModel.isDataImported {
Text("Has data")
} else {
Text("Does not have data")
}
}.onAppear {
NotificationCenter.default.post(name: .onDataImported, object: nil)
}
}
}
如果您正在使用 SwiftUI,您應該考慮使用所有可用的響應式工具並訂閱 Publishers。
SwiftUI 有一個onReceive(_:perform:)
,當它從這個發布者收到一個事件時,它需要一個發布者和閉包來運行。 您可以使用它來偵聽您的通知並基於此更改任何狀態。
這樣做,而不是在 .onAppear 中創建和手動訂閱發布者,意味着您不需要保留可取消的。
import SwiftUI
import Combine
extension NSNotification.Name {
static let onDataImported = Notification.Name("onDataImported")
}
struct ContentView: View {
@State private var dataReceived = false
var body: some View {
VStack {
Text(dataReceived ? "Received" : "Waiting")
.padding()
Button("Simulate Notification") {
NotificationCenter.default.post(name: .onDataImported, object: nil)
}
.padding()
}
.onReceive(NotificationCenter.default.publisher(for: .onDataImported), perform: { _ in
self.dataReceived = true
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
我添加了一個按鈕來發送通知,以便您可以看到它正在被接收。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.