Swift: Toggling checkmark to ON for last selected cell only - not working

1.5k views Asked by At

I have a given number of cells in a section. My goal is to have only the last selected cell to display a checkmark. The other cells should not.

I have found a function in this similar but older thread. I've modified it (and I bet here lies the problem) slightly because of the changes in Swift 3.0.

As it is written below, for me, the function does not work properly. Only the last cell (not last selected, but last in section) in the section will ever get the checkmark. But I cannot figure out why not.

Here is the full function:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let section = indexPath.section
    let numberOfRows = tableView.numberOfRows(inSection: section)
    for row in 0..<numberOfRows {
        if let cell = tableView.cellForRow(at: indexPath) {
            cell.accessoryType = row == indexPath.row ? .checkmark : .none
        }
    }
}

By printing out the values I can see when this statement below evaluates to true, and it makes sense. But the checkmark does not get toggled.

    cell.accessoryType = row == indexPath.row ? .checkmark : .none

Thank you!

2

There are 2 answers

4
Ocunidee On BEST ANSWER

First tell your tableview it can only select one cell at a time:

override func viewDidLoad() {
    super.viewDidLoad()

    self.tableView.allowsMultipleSelection = false
}

Then, let's analyse your code, you're getting the section at which the present cell is being selected and calculating the number of rows in that specific section. You iterate on the rows of that section and checking if you have a cell at the given indexPath (I guess it always evaluates to true because you always have a cell at that indexPath, you didn't put a condition depending on the value of your for-loop). Then you tell the cell to have a checkmark if the row in the for-loop equals the row of the cell being currently selected by the user. As your function is written, there is no reason why only the last in the section would get the checkmark, but you overcomplicated the matter.

Your cell is drawn in the following method and so should the accessory initially.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "YourCell", for: indexPath)

    cell.textLabel?.text = "your text"

    cell.accessoryType = cell.isSelected ? .checkmark : .none
    // cell.selectionStyle = .none if you want to avoid the cell being highlighted on selection then uncomment

    return cell
  }

Then you can just say that the accessory type should be .checkmark in tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) and .none in tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath).Here is how to do it and you should be good, if not let me know and I can edit again.

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .checkmark
}

override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .none
}
0
Abdul Karim On

Swift 4.x
Xcode 12

func viewDidLoad() {
 tableView.allowsMultipleSelection = false
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tvAmbientSoundTableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    tvAmbientSoundTableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}