swiftuitableviewdidselectrowatindexpath

Tableview didSelectRowAt wrongly selects row with JSON data in swift


I am trying to select a tableview row with a checkbox. However, if I select the first cell, then the second cell is selected. If I select the second cell, the third cell is selected, and if I select the third cell, then the first cell is selected. Why is this happening? Can you help me correct this issue?

Code:

extension StudentSignupStep4ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        subscriptionList?.result?.subscriptionData?.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: SubscriptionTableViewCell.cellId, for: indexPath) as! SubscriptionTableViewCell
        let cellData = subscriptionList?.result?.subscriptionData?[indexPath.row]
        cell.subscriptionNameLabel.text = cellData?.subscription_name
     
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let subscriptionData = subscriptionList?.result?.subscriptionData else { return }
        for i in subscriptionData.indices {
            if let cell = tableView.cellForRow(at: IndexPath(row: i, section: 0)) as? SubscriptionTableViewCell {
                cell.isChecked = i == indexPath.row
            }
        }
        tableView.reloadData()
        subsciptionId = subscriptionList?.result?.subscriptionData?[indexPath.row].id
    }
}

class SubscriptionTableViewCell: UITableViewCell {
    
    @IBOutlet weak var checkboxImageView: UIImageView!
    var isChecked = false {
        didSet {
            checkboxImageView.image = UIImage(systemName: (isChecked) ? "checkmark.square.fill": "square")
            checkboxImageView.tintColor = (isChecked) ? .myAccentColor: .lightGray
        }
    }
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

Solution

  • Your error is here:

        for i in subscriptionData.indices {
            if let cell = tableView.cellForRow(at: IndexPath(row: i, section: 0)) as? SubscriptionTableViewCell {
                cell.isChecked = i == indexPath.row
            }
        }
    

    That is completely wrong. Do not talk to the cells directly in this way, ever. Talk only to the data model (subscriptionList?.result?.subscriptionData). Then reload the table view, and let cellForRow read the data model for the individual index path and configure the cell accordingly (as it is already doing).

    And delete the cell's isChecked property. A cell is view, and a highly volatile view at that (because cells, as you've been told, are reused); a cell must not make any attempt to maintain state.