SwiftUI - View 如何知道更新以及为什么 ForEach 需要 id 参数?

问题描述 投票:0回答:2

首先,SwiftUI 视图是否会在必要时自动重绘?

另外,为什么

id
ForEach
的必需参数?

例如,我想用

["a","a","a"]
,
 循环遍历数组 
ForEach

的元素
VStack {
    ForEach(array, id:\.self) { item in
      Text(item)
}

那么假设这个数组变成了

["b","b","b"]
,什么原因导致View被重绘呢?

ForEach
是否简单地识别出数组发生了变化?

swiftui swiftui-foreach identifiable
2个回答
3
投票

它再次循环并再次为每个元素创建一个视图

不要将视图视为 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 函数时),会执行额外的工作来调用视图的主体函数,为视图的描述提供新值,然后可能进行比较更新等等都会发生。


2
投票

在数组为

["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
(紫色)。

对于每个人

  1. 仅当您的数据存在时才使用

    ForEach(0..<array.count) { index in
    static

  2. 如果是

    dynamic
    ,请使用
    Identifiable
    协议:
    ForEach(someIdentifiableArray) { element in

  3. 或使用ID:

    ForEach(someIdentifiableArray, id: \.uniqueProperty) { element in

注意:当

array
包含重复项时,请确保具有用于识别的
id
属性,如
UUID

© www.soinside.com 2019 - 2024. All rights reserved.