首先,SwiftUI 视图是否会在必要时自动重绘?
另外,为什么
id
是ForEach
的必需参数?
例如,我想用
["a","a","a"]
,循环遍历数组
ForEach
的元素
VStack {
ForEach(array, id:\.self) { item in
Text(item)
}
那么假设这个数组变成了
["b","b","b"]
,什么原因导致View被重绘呢?
ForEach
是否简单地识别出数组发生了变化?
它再次循环并再次为每个元素创建一个视图
不要将视图视为 UIKit“UIView”。 SwiftUI 视图只是一个描述。 foreach 正在创建一个值结构,该结构与之前的值结构进行比较。 SwiftUI 运行时使用差异(如果有)来确定如何改变屏幕上的绘图对象,包括对更改进行动画处理等。 SwiftUI 自动计算出如何更改之前绘制的内容,以便获得现在应该绘制的内容。
需要 id,以便您可以从差异中找出具体的更改情况,例如,如果您只知道:
["A", "B", "C"]
->
["B", "C", "C"]
这可能是:
(1) A变为B,B变为C
或
(2) 删除A,然后在末尾添加C。
通过添加 ID,我们可以辨别哪个东西是哪个:
[("A", 1), ("B", 2), ("C", 3)]
->
[("B", 1), ("C", 2), ("C", 3)]
= change A to B, change B to C
= animate cells 1 and 2
或
[("A", 1), ("B", 2), ("C", 3)]
->
[("B", 2), ("C", 3), ("C", 4)]
= delete 1, append 4 = "C"
= animate deletion of 1, shift cells left, animate new cell in from right
SwiftUI 视图如何注意到它需要重新计算 View 主体并计算出是否有任何更改,并可能更新其绘图状态,其神奇之处在于基于
State
、ObservedObject
、StateObject
等属性包装器(不是 ForEach)。 这些 propertyWrappers 像“数组”一样包装变量,这样当它们被设置时(当调用它们的 setter 函数时),会执行额外的工作来调用视图的主体函数,为视图的描述提供新值,然后可能进行比较更新等等都会发生。
在数组为
["a", "a", "a"]
的情况下,您将 id
设置为 \.self
,即 "a"
(即 self)。同样,如果将该数组更改为 ["b", "b", "b"]
,则 id
会更改为 "b"
,从而导致重绘。
我们使用
id
是为了使 array
的行为类似于 Identifiable
协议。试试这个:
ForEach(0..<someArray.count) { index in
...
}.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
self.someArray.append(someElement)
}
}
当您运行上面的代码片段时,
ForEach
不会被重绘。这种行为是不使用 Identifiable
或分配唯一标识符的结果,ForEach
无法识别每个唯一元素,因此它会给你一个 runtime warning
(紫色)。
仅当您的数据存在时才使用
ForEach(0..<array.count) { index in
static
。
如果是
dynamic
,请使用Identifiable
协议:ForEach(someIdentifiableArray) { element in
或使用ID:
ForEach(someIdentifiableArray, id: \.uniqueProperty) { element in
注意:当
array
包含重复项时,请确保具有用于识别的 id
属性,如 UUID
。