我有以下代码:
@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
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以调试并看到通知正在被触发。正在打印“数据已导入”行。我想更新 self.isDataImported 属性,然后刷新我的视图。
if isDataImported {
ShowDataView()
} else {
ProgressView()
}
Run Code Online (Sandbox Code Playgroud)
但视图 ShowDataView 永远不会显示,因为 isDataImported 始终为 false。我缺少什么?
Abi*_*ern 12
如果您使用 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()
}
}
Run Code Online (Sandbox Code Playgroud)
我添加了一个按钮来发送通知,以便您可以看到已收到通知。
SwiftUI 视图的传递性质使得尝试捕获self视图中的引用之类的事情init变得有问题。这里有几个解决方案:
将所有内容保存在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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这通常是我会做的,将其移动到视图模型,这样你就可以保持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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5038 次 |
| 最近记录: |