JAK
JAK

Reputation: 6471

How to get custom cell object from Indexpath?

I tried the following code for getting custom cell.This code I used for the purpose of UITableView cell Check ONLY ONE Row at a Time.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // if they are selecting the same row again, there is nothing to do, just keep it checked
    if ((seleectedIndex) != nil) {

        if indexPath == seleectedIndex {
            return
        }
        let oldCell = tableView.cellForRow(at: seleectedIndex!) as! MisstageTableViewCell
        if oldCell.accessoryType == UITableViewCellAccessoryType.checkmark {
            oldCell.accessoryType = UITableViewCellAccessoryType.none
            oldCell.buttonSelection.setImage(UIImage(named:"off")?.withRenderingMode(.alwaysOriginal), for:.normal)
        }

    }
    // toggle old one off and the new one on
    let newCell = tableView.cellForRow(at: indexPath) as! MisstageTableViewCell
    if newCell.accessoryType == UITableViewCellAccessoryType.none {
        newCell.accessoryType = UITableViewCellAccessoryType.checkmark
        newCell.buttonSelection.setImage(UIImage(named:"on")?.withRenderingMode(.alwaysOriginal), for:.normal)
    }
    seleectedIndex = indexPath  // save the selected index path
}

My code is working fine.But When I scroll the table view (like up and down)then I clicked on any cell then I got the crash on the specific line

let oldCell = tableView.cellForRow(at: seleectedIndex!) as! MisstageTableViewCell

I don’t know what is the reason for crashing.Could you please help me to resolve the issue?

Upvotes: 0

Views: 378

Answers (4)

Suhit Patil
Suhit Patil

Reputation: 12023

You should not change the cell data inside didSelectRow, ideal solution would be to reload the rows based on selected indexPath and check/unckeck accessory type for the cell

class TableViewController: UITableViewController {
    var selectedIndex: IndexPath?

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MisstageTableViewCell
        if let selectedIndex = selectedIndex, indexPath.row == selectedIndex.row {
           cell.accessoryType = .checkmark
           let image = UIImage(named: "on")
           cell.buttonSelection.setImage(image!.withRenderingMode(.alwaysOriginal), for: .normal)
        } else {
          cell.accessoryType = .none
          let image = UIImage(named: "off")

        }
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    //selected the same indexPath then return
      if let selectedIndex = selectedIndex, selectedIndex == indexPath {
        return
      }

      if let selectedIndex = selectedIndex, selectedIndex != indexPath {
         let oldSelectedIndex = selectedIndex
        selectedIndex = indexPath
        tableView.reloadRows(at: [oldSelectedIndex, indexPath], with: .fade)
      } else {
        selectedIndex = indexPath
        tableView.reloadRows(at: [indexPath], with: .fade)
      }
    }
}

Upvotes: 1

Rahul Dasgupta
Rahul Dasgupta

Reputation: 924

why are you updating the cell inside

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

You should do it in

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

First declare a variable as:

var selectedIndexPath: IndexPath!

didSelectRowAt should be modified as follows:

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

    }
}

I don't know what you are doing in cellForRowAt. But it should be modified as follows:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.accessoryType = UITableViewCellAccessoryType.none
    cell.buttonSelection.setImage(UIImage(named:"off")?.withRenderingMode(.alwaysOriginal), for:.normal)
    if selectedIndexPath != nil && indexPath == selectedIndexPath {
        cell.accessoryType = UITableViewCellAccessoryType.checkmark
        cell.buttonSelection.setImage(UIImage(named:"on")?.withRenderingMode(.alwaysOriginal), for:.normal)
    }

    return cell
}

Upvotes: 1

Henry Zhang
Henry Zhang

Reputation: 28

The result of tableView.cellForRow(at: seleectedIndex!) returns is optional, so it`s can be nil

Upvotes: 0

rmickeyd
rmickeyd

Reputation: 1551

You are force unwrapping an optional. If you select a cell then scroll and the selected cell is no longer on screen the system can remove it from memory and thus it will become nil.

if let oldCell = tableView.cellForRow(at: seleectedIndex!) as? MisstageTableViewCell {
    // do stuff here
}

Upvotes: 0

Related Questions