Simon
Simon

Reputation: 6533

UITableviewCell toggling only one custom checkmark state not working - Swift 3

I have reviewed this link and the solutions were not working for me.

I am trying to select one row and when I do select it, it adds a checkmark to the label. If another row is selected while there is an existing checkmark, it will uncheck the previous one which is stored in a selectedIndexPath variable.

It works in the beginning, however when scrolling through the tableview several times, I occasionally see a cell selected that should not be as indicated in this image:

enter image description here

What I am doing when a user selects a cell:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if let cell = tableView.cellForRow(at: indexPath) as? CustomCell {
        let customCell = customCellData[indexPath.row]
        customCell.toggleSelected()
        cell.configureCheckmark(with: customCell)
    }

    if let oldIndexPath = selectedIndexPath, let cell = tableView.cellForRow(at: oldIndexPath) as? CustomCell, oldIndexPath.row != indexPath.row {
        let customCell = customCellData[oldIndexPath.row]
        customCell.toggleSelected()
        cell.configureCheckmark(with: customCell)
    }


    if let selected = selectedIndexPath, selected.row == indexPath.row {
        selectedIndexPath = nil
        tableView.deselectRow(at: indexPath, animated: true)
    } else {
        selectedIndexPath = indexPath
    }

}

and in for cellForRowAt: (is it redundant to check the selectedIndexPath and the state in the model?)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
    let customCell = customCellData[indexPath.row]
    cell.customCell = customCell
    if selectedIndexPath == indexPath {
        cell.checkLabel.text = "✔️"
    } else {
        cell.checkLabel.text = ""
    }

    return cell
}

and finally in the CustomCell:

var customCell: CustomCell? {
    didSet {
        if let customCell = customCell {
            configureCheckmark(with: customCell)
        }
    }
}

func configureCheckmark(with customCell: CustomCellData) {
    if customCell.isSelected {
        checkLabel.text = "✔️"
    } else {
        checkLabel.text = ""
    }
}

In CustomCellData I toggle the state as follows:

class CustomCellData {
    var isSelected = false

    func toggleSelected() {
       isSelected = !isSelected
    }
}

I am scratching my head on this and unsure what to do, any help would be great.

Upvotes: 0

Views: 1021

Answers (1)

vadian
vadian

Reputation: 285290

The easiest solution is to reduce didSelectRowAt to

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
{
    selectedIndexPath = indexPath
    tableView.reloadData()
}

This updates all visible cells properly.

Or more sophisticated version which updates only the affected rows and checks if the cell is already selected

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
    if selectedIndexPath != indexPath  {
        let indexPathsToReload = selectedIndexPath == nil ? [indexPath] : [selectedIndexPath!, indexPath]
        selectedIndexPath = indexPath
        tableView.reloadRows(at: indexPathsToReload, with: .none)
    } else {
        selectedIndexPath = nil
        tableView.reloadRows(at: [indexPath], with: .none)
    }
}

The code in cellForRowAt does the rest.

Upvotes: 1

Related Questions