erd*_*ser 3 swift swiftui combine
假设我的 SwiftUI 应用程序中有一个如下所示的数据模型:
class Tallies: Identifiable, ObservableObject {
let id = UUID()
@Published var count = 0
}
class GroupOfTallies: Identifiable, ObservableObject {
let id = UUID()
@Published var elements: [Tallies] = []
}
Run Code Online (Sandbox Code Playgroud)
我想添加一个GroupOfTallies类似于以下内容的计算属性:
// Returns the sum of counts of all elements in the group
var cumulativeCount: Int {
return elements.reduce(0) { $0 + $1.count }
}
Run Code Online (Sandbox Code Playgroud)
但是,我希望 SwiftUI 在cumulativeCount更改时更新视图。当elements更改(数组获得或丢失元素)或count任何包含的Tallies对象的字段更改时,都会发生这种情况。
我曾考虑将其表示为AnyPublisher,但我认为我对 Combine 的掌握程度不够好,无法使其正常工作。这个答案中提到了这一点,但是从它创建的 AnyPublisher 基于已发布Double而不是已发布Array。如果我尝试不加修改地使用相同的方法,则cumulativeCount只会在元素数组更改时更新,而不会在count元素之一的属性更改时更新。
这里有多个问题需要解决。
首先,重要的是要了解 SwiftUI 在检测到更改时更新视图的主体,无论是在@State属性中,还是来自ObservableObject(通过@ObservedObject和@EnvironmentObject属性包装器)。
在后一种情况下,这是通过@Published属性或手动使用objectWillChange.send(). objectWillChange是ObservableObjectPublisher可在任何ObservableObject.
这是一个很长的说法,如果计算属性的更改与任何属性的更改一起引起@Published- 例如,当从某处添加另一个元素时:
elements.append(Talies())
Run Code Online (Sandbox Code Playgroud)
那么不需要做任何其他事情- SwiftUI 将重新计算观察它的视图,并将读取计算属性的新值cumulativeCount。
当然,如果.count其中一个Tallies对象的属性发生变化,这不会导致 发生变化elements,因为Tallies是引用类型。
给出您的简化示例的最佳方法实际上是使其成为值类型 - a struct:
struct Tallies: Identifiable {
let id = UUID()
var count = 0
}
Run Code Online (Sandbox Code Playgroud)
现在,任何Tallies对象的更改都会导致 的更改elements,这将导致“观察”它的视图获得计算属性的新值。同样,不需要额外的工作。
但是,如果您坚持认为Tallies无论出于何种原因都不能是值类型,那么您需要Tallies通过订阅其.objectWillChange发布者来听取任何更改:
class GroupOfTallies: Identifiable, ObservableObject {
let id = UUID()
@Published var elements: [Tallies] = [] {
didSet {
cancellables = [] // cancel the previous subscription
elements.publisher
.flatMap { $0.objectWillChange }
.sink(receiveValue: self.objectWillChange.send)
.store(in: &cancellables)
}
}
private var cancellables = Set<AnyCancellable>
var cumulativeCount: Int {
return elements.reduce(0) { $0 + $1.count } // no changes here
}
}
Run Code Online (Sandbox Code Playgroud)
以上将通过以下方式订阅elements数组中的更改(以考虑添加和删除):
Sequence为每个数组元素的发布者Tallies对象,到它的objectWillChange发布者objectWillChange.send(),以通知观察它自己更改的视图。| 归档时间: |
|
| 查看次数: |
1818 次 |
| 最近记录: |