Reputation: 10600
I am working with on multiple choose answer quiz app.
var itemsTemp = ["ant","bat", "bear", "bee", "bird", "butterfly", "camel", "cat", "cheetah","chick", "chicken"]
Updated Here is example of my quiz app.
let answerIndex = Int.random(in: 0...3) // for correct answer
itemsTemp!.shuffle() // to multiple choose option change.
here is the error:
Thread 1: Fatal error: each layout item may only occur once
Bellow code crashing
LazyVGrid(columns: [GridItem(),GridItem()],spacing: 16) {
ForEach(itemsTemp) { item in
VStack {
Text(item).id(UUID())
.matchedGeometryEffect(id:UUID(), in: namespace, isSource: !show)
.onTapGesture{
print(" tapped!")
}
}.matchedGeometryEffect(id: "container\(UUID())", in: namespace)
}
}
it's a quiz app so multiple choose option to select correct answer. array item will be occur in for each loop multiple time.
So need to each layout item multiple occur.
Upvotes: 1
Views: 2573
Reputation: 2840
try with a model:
struct Item: Identifiable {
var id = UUID()
var name: String
}
class Model: ObservableObject {
var items: [Item] = [
.init(name: "ant"),
.init(name: "bat"),
.init(name: "ant"),
.init(name: "bear"),
.init(name: "bee"),
.init(name: "ant"),
.init(name: "butterfly"),
.init(name: "bear")
]
@Published var itemsSuffled: [Item] = []
init() {
shuffle()
}
func shuffle() {
itemsSuffled = items.shuffled()
}
}
struct ContentView: View {
@Namespace private var namespace
@ObservedObject private var model: Model = Model()
var body: some View {
VStack {
LazyVGrid(columns: [GridItem(),GridItem()],spacing: 16) {
ForEach(model.itemsSuffled) { item in
VStack {
Text(item.name).id(UUID())
.matchedGeometryEffect(id:UUID(), in: namespace, isSource: true)
.onTapGesture{
print(" tapped!")
}
}.matchedGeometryEffect(id: "container\(UUID())", in: namespace)
}
}
Button(action: {
model.shuffle()
}, label: {
Text("new shuffle")
})
}
}
}
Upvotes: 1
Reputation: 19004
For the unique identifier. Use a model array instead of a string array.
First, create a model.
struct Item: Identifiable {
var id = UUID()
var name: String
}
then, your array is
var itemsTemp: [Item] = [
.init(name: "ant"),
.init(name: "bat"),
.init(name: "ant"),
.init(name: "bear"),
.init(name: "bee"),
.init(name: "ant"),
.init(name: "butterfly"),
.init(name: "bear")
]
Now, your loop is
//Other Code
ForEach(itemsTemp, id:\.id) { item in
//Other Code
}
//Other Code
The second approach is to use .indices
.
//Other Code
ForEach(itemsTemp.indices) { index in
let item = temsTemp[index]
//Other Code
}
//Other Code
Upvotes: 7