如何在SwiftUI视图模型中控制发布的属性值更新?



我有一个具有多个@Published属性的视图模型类。

class AddPassaround : ObservableObject {
@Published var name: String = ""
@Published var reversed : String = ""
@Published var password: String = ""
@Published var age: String = ""
@Published var address: String = ""
@Published var oneAnotherProperty: String = ""
init() {
}
}

当任何一个@Published属性被更新时,我调用API。现在有另一个场景,需要更新多个@Published属性一次编程。像这样

viewModel.name = "test"
viewModel.password = "newPassword"
viewModel.oneAnotherProperty = "notUpdateAll"

现在的问题是API被多次调用,视图被多次重载。在这种情况下,我如何使API只调用一次?在其他情况下应该能正常工作。

SwiftUI面临着同样的问题:当你更新你的ObservableObject的三个单独的@Published属性时,SwiftUI得到三次通知。

SwiftUI通过合并事件避免了三次更新UI。在收到第一个通知时,SwiftUI安排在run循环等待下一个事件之前被唤醒。在后面的通知中,SwiftUI看到它已经被安排唤醒,什么也不做。

UIKit, AppKit和Core Animation也合并显示更新;这就是UIView.setNeedsDisplayCALayer.setNeedsDisplay方法的作用。

你可以使用相同类型的合并。一种方法是使用NotificationQueue:

  • init中,订阅私有Notification.Name和发送方。当通知到达时,调用API。

  • 同样在init中,订阅objectWillChange输出。当objectWillChange发送输出时,要求NotificationQueue.default进入私有通知队列

  • .onName.onSender合并标志传递给NotificationQueue,以便它在事件循环的每轮只发送一次通知。

class AddPassaround : ObservableObject {
@Published var name: String = ""
@Published var reversed : String = ""
@Published var password: String = ""
@Published var age: String = ""
@Published var address: String = ""
@Published var oneAnotherProperty: String = ""
private var tickets: [AnyCancellable] = []
private let notificationSender = NSObject()
private var notificationName: Notification.Name { .init("AddPassaround call API") }
init() {
NotificationCenter.default.publisher(for: notificationName, object: notificationSender)
.sink { [weak self] _ in self?.callAPI() }
.store(in: &tickets)
objectWillChange
.sink { [weak self] _ in self?.scheduleCallAPI() }
.store(in: &tickets)
}
private func scheduleCallAPI() {
// Arrange to callAPI soon, if I haven't already arranged it.
NotificationQueue.default.enqueue(
.init(name: notificationName, object: notificationSender),
postingStyle: .whenIdle,
coalesceMask: [.onName, .onSender],
forModes: [.common]
)
}
private func callAPI() {
print("this is where you call the API")
}
}

我们使用一个单独的对象notificationSender,而不是self,以避免selfNotificationCenter.default之间的保留循环。

如果你只想让你的一些属性触发API调用,你可以给他们willSet(或didSet)观察者,而不是订阅objectWillChange:

class AddPassaround : ObservableObject {
@Published var name: String = "" {
willSet { scheduleCallAPI() }
}
@Published var reversed : String = ""
@Published var password: String = "" {
willSet { scheduleCallAPI() }
}
@Published var age: String = ""
@Published var address: String = ""
@Published var oneAnotherProperty: String = ""
private var tickets: [AnyCancellable] = []
private let notificationSender = NSObject()
private var notificationName: Notification.Name { .init("AddPassaround call API") }
init() {
NotificationCenter.default.publisher(for: notificationName, object: notificationSender)
.sink { [weak self] _ in self?.callAPI() }
.store(in: &tickets)
}
private func scheduleCallAPI() {
// Arrange to callAPI soon, if I haven't already arranged it.
NotificationQueue.default.enqueue(
.init(name: notificationName, object: notificationSender),
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
}
private func callAPI() {
print("this is where you call the API")
}
}

相关内容

  • 没有找到相关文章

最新更新