Nazmul Hasan
Nazmul Hasan

Reputation: 10600

How to allow ForEach layout item to show duplicate item from array in SwiftUI?

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

Answers (2)

Simone Pistecchia
Simone Pistecchia

Reputation: 2840

try with a model:

enter image description here

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

Raja Kishan
Raja Kishan

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

Related Questions