skip to Main Content

I am trying to select a single cell for each section. Everything works fine but when I scroll tableview to the bottom or top the checkmark accessory appears on random cells. Here is the code:

tableView.allowsMultipleSelection = true

    func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
          // Find any selected row in this section
          if let selectedIndexPath = tableView.indexPathsForSelectedRows?.first(where: { $0.section == indexPath.section}) {
              // Deselect the row
              tableView.deselectRow(at: selectedIndexPath, animated: false)
              // deselectRow doesn't fire the delegate method so need to
              // unset the checkmark here
              tableView.cellForRow(at: selectedIndexPath)?.accessoryType = .none
          }
          return indexPath
      }

       func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? {
          // Prevent deselection of a cell
          return nil
      }

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

any help would be appreciated to avoid this

2

Answers


  1. That is happening because you are making changes to on-screen cells without updating your model to reflect those changes. Then when a cell gets recycled, sometimes you pick up a cell that still has it’s checkmark set.

    Don’t reach into cells and change their appearance. Instead, mark the cell as needing to be updated (by calling reloadRows(at:with)) and then in your tableView(cellForRowAt:) method, always set the accessory type to either .none or .checkmark.

    Login or Signup to reply.
  2. This happens because cells are reusable. I don’t think its a good idea to call tableView.cellForRow in didSelectRowAt.You must control which indexPath are selected in cellForRowAt

    Try this :

     var selectedRows = [IndexPath]() // selected indexPaths array
       func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if selectedRows.contains(indexPath){ // if already selected , remove
            selectedRows = selectedRows.filter({$0 != indexPath})
        }else{
            selectedRows.append(indexPath)
        }
        tableView.reloadRows(at: [indexPath], with: .automatic)
      }
    

    And in your cellForRowAt

      if selectedRows.contains(indexPath){
            cell.accessoryType = .checkmark
        }else{
            cell.accessoryType = .none
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search