TL;DR
Your ForEach needs id: .self
added after your range.
Explanation
ForEach has several initializers. You are using
init(_ data: Range<Int>, @ViewBuilder content: @escaping (Int) -> Content)
where data
must be a constant.
If your range may change (e.g. you are adding or removing items from an array, which will change the upper bound), then you need to use
init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content)
You supply a keypath to the id
parameter, which uniquely identifies each element that ForEach
loops over. In the case of a Range<Int>
, the element you are looping over is an Int
specifying the array index, which is unique. Therefore you can simply use the .self
keypath to have the ForEach identify each index element by its own value.
Here is what it looks like in practice:
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])")
//Note: If you want more than one views here, you need a VStack or some container, or will throw errors
}
}
}
}
If you run that, you'll see that as you press the button to add items to the array, they will appear in the VStack
automatically. If you remove "id: .self
", you'll see your original error:
`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable` or use `ForEach(_:id:content:)`
and provide an explicit `id`!"
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…