swift - 在 SwiftUI 模型对象中发布的计算属性
问题描述
假设我的 SwiftUI 应用程序中有一个数据模型,如下所示:
class Tallies: Identifiable, ObservableObject {
let id = UUID()
@Published var count = 0
}
class GroupOfTallies: Identifiable, ObservableObject {
let id = UUID()
@Published var elements: [Tallies] = []
}
我想添加一个计算属性GroupOfTallies
,类似于以下内容:
// Returns the sum of counts of all elements in the group
var cumulativeCount: Int {
return elements.reduce(0) { $0 + $1.count }
}
但是,我希望 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())
那么就不需要做任何其他事情了——SwiftUI 将重新计算观察它的视图,并读取计算属性的新值cumulativeCount
。
当然,如果.count
其中一个对象的属性Tallies
发生变化,这不会导致 的变化elements
,因为Tallies
它是一个引用类型。
给出您的简化示例的最佳方法实际上是使其成为值类型 - a struct
:
struct Tallies: Identifiable {
let id = UUID()
var count = 0
}
现在,任何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
}
}
以上将通过以下方式订阅elements
数组中的更改(以考虑添加和删除):
- 将数组转换
Sequence
为每个数组元素的发布者 - 然后 flatMap 再次将每个数组元素(这是一个
Tallies
对象)放入其objectWillChange
发布者 - 然后对于任何输出,调用
objectWillChange.send()
,通知观察它自己的变化的视图。
推荐阅读
- javascript - 如何通过 Javascript 提交带有自定义变量的表单?
- c++ - 从 initializer_list 初始化 std::array
- reactjs - 在 Grommet DataTable Reactjs 上设置唯一键
- c - 如何处理分配的字符串输入?
- php - 如何显示 php myadmin 记录数
- python - 如何减少 OpenCV python 中的帧数?
- regex - RegEx 用于出现一个常量,后跟任何字符串,直到空格或字符串结尾
- c# - 添加文本的次数与文本中的字符数一样多
- c - 根据文件大小分配内存没有正确的数字?
- html - justify-content 和 flex-grow 被忽略