ForEach:ID 参数和闭包返回类型



所以,我正在浏览 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"。

我希望这是(一些)有意义的。


关于一些评论的补充说明:

在我看来,混乱的主要来源是关闭本身。因此,让我们尝试其他方法。让我们编写不带闭包的相同代码:

ForEachinit有这样的签名:

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)
}
}

我希望这可以帮助更好地澄清事情。

最新更新