Home > Net >  iOS16 SwiftUI app crashes when trying to create view using loop
iOS16 SwiftUI app crashes when trying to create view using loop

Time:10-03

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)")
                }
            }
        }
    }
}

CodePudding user response:

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])")
                }
            }
        }

    }
}

CodePudding user response:

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.

  • Related