我有以下组件可呈现半透明字符的网格:
var body: some View {
VStack{
Text("\(self.settings.numRows) x \(self.settings.numColumns)")
ForEach(0..<self.settings.numRows){ i in
Spacer()
HStack{
ForEach(0..<self.settings.numColumns){ j in
Spacer()
// why do I get an error when I try to multiply i * j
self.getSymbol(index:j)
Spacer()
}
}
Spacer()
}
}
}
settings
是一个EnvironmentObject
只要更新settings
,最外面的VStack
中的文本就会正确更新。但是,视图的其余部分不会更新(Grid具有与以前相同的尺寸)。为什么会这样?
第二个问题:
为什么无法访问内部i
循环中的ForEach
并将其作为参数传递给函数?
在外部ForEach
循环中出现错误:
无法推断出通用参数“数据”
答案 0 :(得分:2)
您的ForEach需要在您的范围之后添加id: \.self
。
ForEach有several initializers。您正在使用
init(_ data: Range<Int>, @ViewBuilder content: @escaping (Int) -> Content)
其中data
必须为常数。
如果您的范围可能会发生变化(例如,您正在从数组中添加或删除项目,这会更改上限),那么您需要使用
init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content)
您提供了id
参数的键路径,该参数唯一地标识了ForEach
循环的每个元素。对于Range<Int>
,要循环的元素是一个Int
,它指定唯一的数组索引。因此,您可以简单地使用\.self
键路径,让ForEach通过其自己的值来标识每个索引元素。
这是实际的样子:
struct ContentView: View {
@State var array = [1, 2, 3]
var body: some View {
VStack {
Button("Add") {
self.array.append(self.array.last! + 1)
}
// this is the key part v--------v
ForEach(0..<array.count, id: \.self) { index in
Text("\(index): \(self.array[index])")
}
}
}
}
如果运行该命令,则会看到按下按钮向数组添加项目时,它们将自动出现在VStack
中。如果删除“ id: \.self
”,则会看到原始错误:
`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable` or use `ForEach(_:id:content:)`
and provide an explicit `id`!"
答案 1 :(得分:1)
ForEach仅应用于恒定数据。因此,根据定义它仅被评估一次。尝试将其包装在列表中,您将看到错误记录,例如:
ForEach,Int,TupleView <(Spacer,HStack,Int,TupleView <(Spacer,Text,Spacer)>>>,Spacer)>> count(7)!=其初始计数(0)。
ForEach(_:content:)
仅应用于恒定数据。而是使数据符合Identifiable
或使用ForEach(_:id:content:)
并提供明确的id
!
我也对此感到惊讶,并且无法找到有关此限制的任何官方文档。
关于为什么您无法在内部ForEach循环中访问i的原因,我认为您可能会遇到误导性的编译器错误,这与代码段中缺少的其他内容有关。最好的猜测(Xcode 11.1,Mac OS 10.14.4)完成后,它确实为我编译了。
这是我想出的办法,目的是使您的ForEach能够通过一些可识别的事情:
struct SettingsElement: Identifiable {
var id: Int { value }
let value: Int
init(_ i: Int) { value = i }
}
class Settings: ObservableObject {
@Published var rows = [SettingsElement(0),SettingsElement(1),SettingsElement(2)]
@Published var columns = [SettingsElement(0),SettingsElement(1),SettingsElement(2)]
}
struct ContentView: View {
@EnvironmentObject var settings: Settings
func getSymbol(index: Int) -> Text { Text("\(index)") }
var body: some View {
VStack{
Text("\(self.settings.rows.count) x \(self.settings.columns.count)")
ForEach(self.settings.rows) { i in
VStack {
HStack {
ForEach(self.settings.columns) { j in
Text("\(i.value) \(j.value)")
}
}
}
}
}
}
}