Polis Polis - 4 months ago 107
Swift Question

tableView.cellForRowAtIndexPath returns nil with too many cells (swift)

So I have the weirdest thing;

I am looping a tableView in order to iterate over all cells. It works fine with less than 5 cells, but crashes with "unexpectedly found nil" for more cells. Here's the code:

for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberofRowsInSection(section) {
let indexPath = NSIndexPath(forRow: row, inSection: section)
let cell = tableView?.cellForRowAtIndexPath(indexPath) as? MenuItemTableViewCell

// extract cell properties


The last line is the one that gives the error.

Any thoughts?

Answer

Because cells are reused, cellForRowAtIndexPath will give you cell only if cell for given indexPath is currently visible. It is indicated by the optional value. If you want to prevent from crash, you should use if let

if let cell = tableView?.cellForRowAtIndexPath(indexPath) as? MenuItemTableViewCell {
     // Do something with cell
}

If you want to update values from cell, your cells should update the dataSource items. For example you can create delegate for that

protocol UITableViewCellUpdateDelegate {
    func cellDidChangeValue(cell: UITableViewCell)
}

Add delegate to your cell and suppose we have a textField in this cell. We add target for the didCHangeTextFieldValue: for EditingDidChange event so it is called every time the user types somethink in it. And when he do, we call the delegate function.

class MyCell: UITableViewCell {
    @IBOutlet var textField: UITextField!

    var delegate: UITableViewCellUpdateDelegate?

    override func awakeFromNib() {
        textField.addTarget(self, action: Selector("didCHangeTextFieldValue:"), forControlEvents: UIControlEvents.EditingChanged)
    }

    @IBAction func didCHangeTextFieldValue(sender: AnyObject?) {
        self.delegate?.cellDidChangeValue(cell)
    }
}

Then in cellForRowAtIndexPath you add the delegate

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier", forIndexPath: indexPath)
    cell.delegate = self

    return cell
}

And finally we implement the delegate method:

func cellDidChangeValue(cell: UITableViewCell) {

    guard let indexPath = self.tableView.indexPathForCell(cell) else {
        return
    }

    /// Update data source - we have cell and its indexPath

}

Hope it helps

Comments