所以,我正在浏览 SwiftUI 文档以熟悉。我正在开发一个网格示例应用程序。它具有以下代码:
ForEach(allColors, id: .description) { color in
Button {
selectedColor = color
} label: {
RoundedRectangle(cornerRadius: 4.0)
.aspectRatio(1.0, contentMode: ContentMode.fit)
.foregroundColor(color)
}
.buttonStyle(.plain)
}
我一开始并没有想到 ForEach 实际上是一个结构体,起初我认为它是 for in 循环的变体,所以我对此很陌生。然后我检查了文档。
当我阅读 ForEach 结构的文档和一些谷歌文章时,我不明白代码中的两点:
- 因此,我们正在用颜色数组初始化foreach结构。对于 ID,为什么他们使用
.description
而不是 .self? - 二是使用
color in
。由于 foreach 是一个结构体,而 paranthesis 是初始化参数,这看起来像闭包的返回类型,但我们为什么要将单个颜色返回给 foreach?我认为返回是视图或控件(如按钮和标签)的集合。这就像var anInteger: Int = 1
例如。由于关闭,ForEach 接受什么类型?还是我看错了?
所以我们用一个颜色数组初始化 foreach 结构。对于 ID,为什么他们使用 .\description 而不是 .self?
这取决于allColors
的类型。您应该记住,这里的id
应该是稳定的。文档指出:
替换为具有新标识的新数据元素,否则数据元素的 id 不会更改,这一点很重要。如果数据元素的 id 发生更改,则从该数据元素生成的内容视图将丢失任何当前状态和动画。
因此,例如,如果颜色是引用类型(可识别的),并且您将一个对象与相同的对象交换(就字段值而言),则标识将更改,而description
不会(出于此示例的目的 - 只是假设我无法访问的代码意图)。
编辑:另请注意,在此特定示例中,allColors
似乎是Color
列表,无法识别。这就是自定义id
键路径背后的原因。
关于第二点,请注意尾随闭包也是一个初始化参数。为了清楚地看到这一点,我们可以使用"无糖"版本:
ForEach(allColors, id: .description, content: { color in
Button {
selectedColor = color
} label: {
RoundedRectangle(cornerRadius: 4.0)
.aspectRatio(1.0, contentMode: ContentMode.fit)
.foregroundColor(color)
}
.buttonStyle(.plain)
})
其中content
是一个闭包(一个匿名函数),它传递集合的一个元素并返回一些View
。
所以这个想法是这样的:"给我一个可识别元素的集合,我将为每个元素调用一个函数,期望你返回我some View
"。
我希望这是(一些)有意义的。
关于一些评论的补充说明:
在我看来,混乱的主要来源是关闭本身。因此,让我们尝试其他方法。让我们编写不带闭包的相同代码:
ForEach
的init
有这样的签名:
init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content)
现在,content
转换为:
一个具有一个类型为Data.Element
的参数的函数,在我们的例子中是从data
推断出来的,因此它是一个Color
。该函数的返回类型是Content
这是一个生成some View
的视图生成器
因此,我们的最终代码(相当于第一个代码)可能如下所示:
struct MyView: View {
let allColors: [Color] = [.red, .green, .blue]
@State private var selectedColor: Color?
var body: some View {
List {
ForEach(allColors, id: .description, content: colorView)
}
}
@ViewBuilder
func colorView(color: Color) -> some View {
Button {
selectedColor = color
} label: {
RoundedRectangle(cornerRadius: 4.0)
.aspectRatio(1.0, contentMode: ContentMode.fit)
.foregroundColor(color)
}
.buttonStyle(.plain)
}
}
我希望这可以帮助更好地澄清事情。