zach2161 zach2161 - 5 months ago 9
Swift Question

Index out of range for indexPath swift

I am making an app that requires the user to tap on multiple cells in order to select them. when they tap on a cell, a .Checkmark accessory item will appear. For some reason though whenever I try and get to that VC the app crashes and I get the following error messages on line 8(if !checked[indexPath.row]):Bad Instruction error

Index out of range

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: InstrumentTableCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? InstrumentTableCell


cell.configurateTheCell(recipies[indexPath.row])

if !checked[indexPath.row] {
cell.accessoryType = .None
} else if checked[indexPath.row] {
cell.accessoryType = .Checkmark
}
return cell
}


and this is my working checked method:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
checked[indexPath.row] = false
} else {
cell.accessoryType = .Checkmark
checked[indexPath.row] = true
}
}
}

Answer

Your problem is that you only store items in your checked array in the func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) and that method is only called when you actually select a row.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell on the other hand is called each and every time you need to render a new table cell.

So when you in cellForRowAtIndexPath say:

if !checked[indexPath.row]

then you can not be sure that checked actually contains anything. So for instance the first time you start rendering your cells, your checked array does not contain any values and therefore it crashes when you ask it for a value on a position that it does not have a value for.

One solution could be to initialize your checked array to contain all false values. I'm guessing you have some model array called recipies so you could do something like:

for (index, _) in recipies.enumerate() {
    checked.append(false)
}

that way you are sure that your checked array is properly initialized with the same number of elements that you have recipies. (and yes, someone probably has a better way to do this).

Another option could be to let the individual elements in recipies know whether or not they are checked.

Hope this makes sense and helps you.