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 var
s 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.