Satsuki
Satsuki

Reputation: 2196

didSelectRowAt is not working as intended

I am having trouble putting a checkmark for a cell. I have placed a checklabel in the cell. When the user taps the cell the label displays ● and when the user taps the cell again it should display ○. However when I tap on a cell for example index path 0,10, then both 0,10 and 0,5 displays ●. Why does this happen? Any help is appreciated.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if isEditingSetlist{
        if let cell = booklistDetailTableView.cellForRow(at: indexPath) as? BooklistTableViewCell {
            if cell.checkLabel.text == "●"{
                cell.checkLabel.text = "○"
            }else{
                cell.checkLabel.text = "●"
            }
        }
        selectedRows.append(indexPath.row)
    }
}

Upvotes: 0

Views: 163

Answers (4)

AMIT
AMIT

Reputation: 924

UITableViewCell is a reference type. So when you will change UILabel value inside the cell, it would be applied for all other references. To avoid this problem, you can use an array.

Try this code Example:

ViewController.swift file

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
    @IBOutlet weak var tavleView: UITableView!
    var items: [Book]?

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let item = items?[indexPath.row] {
            item.isCheck = !item.isCheck
            tableView.reloadRows(at: [indexPath], with: .none)
        }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items?.count ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? BooklistTableViewCell {
            if let item = items?[indexPath.row] {
                if !item.isCheck {
                    cell.checkLabel.text = "○ \(item.isCheck) \(indexPath.row)"
                }else{
                    cell.checkLabel.text = "● \(item.isCheck) \(indexPath.row)"
                }
            }
            return cell
        }
        return UITableViewCell()
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        items = (0..<20).map({ _ in Book(false) })
        tavleView.delegate = self
        tavleView.dataSource = self
        tavleView.reloadData()
    }
}

BooklistTableViewCell.swift file

import UIKit

class BooklistTableViewCell: UITableViewCell {
    @IBOutlet weak var checkLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

Book.swift file

import Foundation

class Book {
    var isCheck = false

    init(_ isCheck: Bool) {
        self.isCheck = isCheck
    }
}

Upvotes: -1

koen
koen

Reputation: 5729

You can also change the checkmark in willDisplayCell. This is an ideal place to change the appearance of a cell, just before it will be rendered. From the docs:

A table view sends this message to its delegate just before it uses cell to draw a row, thereby permitting the delegate to customize the cell object before it is displayed. This method gives the delegate a chance to override state-based properties set earlier by the table view, such as selection and background color. After the delegate returns, the table view sets only the alpha and frame properties, and then only when animating rows as they slide in or out.

So for your case, it could be something like:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
     if cell.checkLabel.text == "●" {
       cell.checkLabel.text = "○"
     } else {
        cell.checkLabel.text = "●"
     }
 }

Upvotes: 1

vadian
vadian

Reputation: 285069

Cells are reused. The most efficient solution is to add an isSelected property to the data model for example

struct Model {

   var isSelected = false

   // other properties
}

In cellForRowAt set the checkmark accordingly (dataSource represents the data source array)

let item = dataSource[indexPath.row]
cell.checkLabel.text = item.isSelected ? "●" : "○"

In didSelectRow toggle isSelected and reload the row (which calls cellForRowAt)

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if isEditingSetlist{
        dataSource[indexPath.row].isSelected.toggle()
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
}

And forget selectedRows. Extra arrays become annoying if cells can be inserted, deleted or moved.

Upvotes: 6

Jawad Ali
Jawad Ali

Reputation: 14397

In didSelect method just append and remove index path from array ... if that indexPath already exist ... remove that from array ... otherwise append.. then reload tableView

And in your cellForRow method check if that indexPath exists in selectedRows array put "●" otherwise put "○" to cell.checkLabel.text

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
       if selectedRows.contains(indexPath.row) {
    //remove it from array
if let index = selectedRows.index(where: { $0 == indexPath.row }) {
      selectedRows.remove(at: index)

    }
    }else {
            selectedRows.append(indexPath.row)
        }
    tableView.reloadData()
    }

Upvotes: 1

Related Questions