skip to Main Content

I’ve defined a view in SwiftUI which takes an Int array and is supposed to display the elements of the array in a VStack such that every "full row" contains three elements of the array and then the last "row" contains the remainder of elements. When running the app on iOS16 I get "Fatal error: Can’t remove first element from an empty collection" for the call let die = dice.removeFirst() (also when passing in a non-empty array of course). I’ve tried following the debugger but I don’t understand the way it jumps around through the loops.

On iOS15 this code worked fine. In the actual program I don’t display the array content as Text but I have images associated with each Int between 1 and 6 which I display. I replaced this with Text for simplicity’s sake.

Thanks for any help!

struct DiceOnTableView: View {
    let diceArray: [Int]
    
    var body: some View {
        let fullRows: Int = diceArray.count / 3
        let diceInLastRow: Int = diceArray.count % 3
        var dice: [Int] = diceArray
        
        VStack {
            ForEach(0..<fullRows, id: .self) { row in
                HStack {
                    ForEach(0..<3) { column in
                        let die = dice.removeFirst()
                        Text("(die)")
                    }
                }
            }
            
            HStack {
                ForEach(0..<diceInLastRow, id: .self) { column in
                    let die = dice.removeFirst()
                    Text("(die)")
                }
            }
        }
    }
}

2

Answers


  1. This does kind of work on iOS 15 (but strangely – the order of the dice is unexpected), and crashes on iOS 16. In general, you should not be using vars in SwiftUI view building code.

    Your code can be modified to compute the index into the original diceArray from the row, fullRows, and column values:

    struct DiceOnTableView: View {
        let diceArray: [Int]
    
        var body: some View {
            let fullRows: Int = diceArray.count / 3
            let diceInLastRow: Int = diceArray.count % 3
    
            VStack {
                ForEach(0..<fullRows, id: .self) { row in
                    HStack {
                        ForEach(0..<3) { column in
                            Text("(diceArray[row * 3 + column])")
                        }
                    }
                }
    
                HStack {
                    ForEach(0..<diceInLastRow, id: .self) { column in
                        Text("(diceArray[fullRows * 3 + column])")
                    }
                }
            }
    
        }
    }
    
    Login or Signup to reply.
  2. The ForEach View will crash if you use it like a for loop with dynamic number of items. You need to supply it an array of item structs with ids, e.g. one that is Identifiable, e.g.

    struct DiceItem: Identifable {
        let id = UUID()
        var number: Int
    }
    
    @State var diceItems: [DiceItem] = []
    
    ForEach(diceItems) { diceItem in 
    

    And perhaps use a Grid instead of stacks.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search