1

I have a table view with 2 (or more) sections. I have added an ImageView into it and need to change the image view according the values which are containing in an array at the beginning and when selecting/deselecting a cell. I created the view as follows,

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let viewHeader = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 40))
    buttonCheck = UIButton(type: .custom)
    buttonCheck!.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
    buttonCheck!.tag = section
    buttonCheck!.addTarget(self, action: #selector(tapSection(sender:)), for: .touchUpInside)
    viewHeader.addSubview(buttonCheck!)
}

This adds the ImageView fine and when I load the table data initially, I need to set the image view programatically. To change the image view I did,

if tableViewData.contains(where: self.tags.contains) {
   buttonCheck!.setImage(UIImage(named: "CheckmarkCircle"), for: .normal)
} else {
   buttonCheck!.setImage(UIImage(named: "DeselectedCheckmarkCircle"), for: .normal)
}

I called this inside didSelectRowAt and didDeselectRowAt method. The problem here is, When I select a cell from section one(section = 0), it affects to the second section(section = 1) header image view. In other work when I select a cell from section one the second section's header image is changing. How may I fix this?

codebot
  • 2,540
  • 3
  • 38
  • 89

2 Answers2

3

I believe the problem is that you are overriding the buttonCheck every time viewForHeaderInSection is called, meaning it will always hold a reference to the last button you created.

It would be better if you created a dictionary to hold the imageviews (where the index is the section), like this on the controller scope:

var imageViews: [Int: UIButton] = [:]

Then change viewForHeaderInSection to this:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let viewHeader = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 40))
    let buttonCheck = UIButton(type: .custom)
    buttonCheck!.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
    buttonCheck!.tag = section
    buttonCheck!.addTarget(self, action: #selector(tapSection(sender:)), for: .touchUpInside)
    imageViews[section] = buttonCheck
    viewHeader.addSubview(buttonCheck!)
}

Then on the didSelect and didDeselect update the imageView:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    imageViews[indexPath.section]?.setImage(UIImage(named: "CheckmarkCircle"), for: .normal)
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    imageViews[indexPath.section]?.setImage(UIImage(named: "DeselectedCheckmarkCircle"), for: .normal)
}

Please take into account that performance wise, this might not be the best solution. It would be better create a custom view extending UITableViewHeaderFooterView and take into account view reusablity.

Claudio
  • 5,078
  • 1
  • 22
  • 33
  • Just for my curiosity: are headers non-reusable? This approach would only work correctly if all headers would only be created once and never reused. – lennartk Jun 08 '20 at 15:15
  • @lennartk headers can also be reused. Yes. I believe that if the views were reused, the dictionary would have different sections pointing to the same view. – Claudio Jun 08 '20 at 19:21
2

There are multiple approaches you can take. The quick and dirty way is to just call tableView.reloadData(), which will force every element in your TableView to be reloaded with the current data from your DataSource.

If you want to take a more performant approach, you could choose to only reload the section headers by looping over them. This is very well answered in this question. Good luck.

lennartk
  • 570
  • 1
  • 4
  • 15