xyzcodeeee
xyzcodeeee

Reputation: 171

SwiftUI Grid Index out of Range

I try this solution of using Grid layout.

We want to display dynamically Array Items in a Grid and if the Array.count Changed an error with Index out of Range comes up and the App crashes.

How to fix this?

var totalrows: Int{
    let t = Double(self.cards.count) / Double(self.cols)
    return Int(round(t))
}


var cols: Int{
    let col = self.verticalSizeClass == .compact ? 4 : 2
    return col
}



func colrow (col: Int , row: Int) -> Int{
    var colrow = 0
        colrow = (row * self.cols) + col
    return colrow
}






let cards = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"]



var body: some View {

        VStack{
            ForEach(0..<self.totalrows,id:\.self) { row in
                HStack {
                    ForEach(0..<self.cols,id:\.self) { column in
                        Text(self.cards[self.colrow(col: column, row: row)])
                    }


                }
               }
    }
}

Upvotes: 0

Views: 514

Answers (3)

Paul B
Paul B

Reputation: 5115

If you add custom safe subscript you'll be able to replace index out boudns array elements with anything you like using nil coalescing.

extension Array {
   subscript(guarded idx: Int) -> Element? {
        guard (startIndex..<endIndex).contains(idx) else { return nil }
        return self[idx]
    }
}

Then you could rewrite Text view like this to display hyphens for invalid indexes and have no crashes.

//...
ForEach(0..<self.cols,id:\.self) { column in
                        Text(self.cards[guarded: self.colrow(col: column, row: row)] ?? "-")
                    }
//...

Upvotes: 0

Quinn
Quinn

Reputation: 9434

An easy way to avoid any indexOutOfBounds is just to check if the index is out of bounds before doing the operation...

So make this change:

ForEach(0..<self.cols,id:\.self) { column in
    let card = self.colrow(col: column, row: row)
    if (card < self.cards.count) {
        Text(self.cards[card])
    }
}

This will leave your last row potentially unfilled but it shouldn't crash

Upvotes: 2

Dawson Loudon
Dawson Loudon

Reputation: 6029

The way you have cols return either 4 or 2, you will have to have an even count in cards.

I would solve this by always checking the count of cards and adding an empty item to the end if not already even.

Example:

//only even numbers count
var cards = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"]

if (cards.count % 2) != 0 {
    cards.add("")
}

Upvotes: 0

Related Questions