1

I have a TablewView that has prototype UITableViewCell that has its own class,

Custom UITableViewCell Class

import UIKit

class H_MissingPersonTableViewCell: UITableViewCell {

    @IBOutlet weak var strName: UILabel!
    @IBOutlet weak var imgPerson: UIImageView!
    @IBOutlet weak var Date1: UILabel!
    @IBOutlet weak var Date2: UILabel!
    @IBOutlet weak var MainView: UIView!
    @IBOutlet weak var btnGetUpdates: UIButton!
    @IBOutlet weak var btnComment: UIButton!
    @IBOutlet weak var btnReadMore: UIButton!
    
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

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

        // Configure the view for the selected state
    }

}

The TableView loads perfectly and the dataSource and delegate work fine. However, when I click a BUTTON at IndexPath.row = 0 (zero), and then I scroll down , I would see random(?) or other cells also being highlighted as a result of clicking BUTTON in row 0...

Here is my CellForRowAtIndexPath code:

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        println("called")
        var cell : CustomCell
        
        
        cell = tableView.dequeueReusableCellWithIdentifier("CustomCellID", forIndexPath: indexPath) as! CustomCell
        cell.strName.text = self.names[indexPath.row]
        cell.imgPerson.image = UIImage(named: "\(self.persons[indexPath.row])")!

        cell.btnGetUpdates.layer.borderColor = UIColor.darkGrayColor().CGColor
        cell.btnGetUpdates.layer.borderWidth = 1
        cell.btnGetUpdates.layer.cornerRadius = 5.0
        

        cell.btnComment.layer.borderColor = UIColor.darkGrayColor().CGColor
        cell.btnComment.layer.borderWidth = 1
        cell.btnComment.layer.cornerRadius = 5.0
        
        cell.btnReadMore.layer.borderColor = UIColor.darkGrayColor().CGColor
        cell.btnReadMore.layer.borderWidth = 1
        cell.btnReadMore.layer.cornerRadius = 5.0
        
        cell.dateMissingPersonPlace.text = self.MissingPeoplePlace[indexPath.row]
        cell.dateMissingPersonSince.text = self.MissingPeopleSince[indexPath.row]
        
        cell.btnGetUpdates.tag = indexPath.row
        cell.btnGetUpdates.addTarget(self, action: "GetUpdatesButton:", forControlEvents: .TouchUpInside)
        
        return cell
        
    }

in my cell.btnGetUpdates , I put an action as you can see in the last part of my CellForIndex code, GetUpdatesButton:

This is the code:

func GetUpdatesButton(sender: UIButton){
    println("Button Clicked \(sender.tag)")

    var sen: UIButton = sender
    var g : NSIndexPath = NSIndexPath(forRow: sen.tag, inSection: 0)
    var t : CustomCell = self.myTableView.cellForRowAtIndexPath(g) as! CustomCell
    t.btnGetUpdates.backgroundColor = UIColor.redColor()
    }

The problem is, NOT ONLY the button in index 0 is being highlighted, but I would also see random buttons from other index/rows being highlighted as I scroll my tableView down.

Where did I go wrong, how can I only update the button I clicked...and not other buttons..

I have 20 items ( 20 rows) and when I clicked button in row 0, rows 3, 6, 9....are also having highlighted buttons.

ROW 0 , button clicked

ROW ZERO , button clicked

ROW 3, button clicked as well, though I did not really click it. enter image description here

Community
  • 1
  • 1
  • 1
    This is a very common problem - you will find lots of questions about it here on SO. Cells are re-used as you scroll, so you cannot store status data in the cell object. You have to store the status in some other data model and use that to set the button status in `cellForRowAtIndexPath` - i.e. turn it off if it should be off and turn it on if it should be on. – Paulw11 Sep 14 '15 at 05:57
  • 2
    You can see my answer here - http://stackoverflow.com/questions/27918584/uitableview-checkmarks-disappear-when-scrolling/27919322#27919322 - That answer sets a checkmark but you can use the same approach with a button handler and setting the button color. The main idea is using an NSMutableIndexSet to store the selected items – Paulw11 Sep 14 '15 at 07:43
  • Hi, thanks a lot! I'll take a look at this and let you know for a while :) – Raymond Led Carasco Sep 14 '15 at 07:49
  • I can't add button.tag to the NSMutableIndexSet array ... – Raymond Led Carasco Sep 14 '15 at 07:59
  • Hi my problem is not solved, thanks for answering, it helped me grasp the concept, I used an INT array and saved the indexPath.row there and then compare it in the CellForRowAtIndex – Raymond Led Carasco Sep 14 '15 at 08:15
  • I've edited my post to include the solution I used. – Raymond Led Carasco Sep 14 '15 at 08:17
  • I suggest you look at NSMutableIndexSet - it is a better fit than an int array, but otherwise it is much the same – Paulw11 Sep 14 '15 at 08:53
  • Yes, I will, and thank you for your suggestion :) – Raymond Led Carasco Sep 14 '15 at 08:58

5 Answers5

5

This is occurring due to UITableview have reusablecell policy.In order to resolve this issue You need to maintain one array of selected items in cellForRowAtIndexPath method you have to verify weather this button is being hold by selected item array. if yes then apply selection styles otherwise apply normal style to it.

Check below source code for buttonclick:

func GetUpdatesButton(sender: UIButton) 
{
    var sen: UIButton = sender
    var g : NSIndexPath = NSIndexPath(forRow: sen.tag, inSection: 0)
    var t : CustomCell = self.myTableView.cellForRowAtIndexPath(g) as!   CustomCell
    t.btnGetUpdates.backgroundColor = UIColor.redColor()
   self.selectedButtonsArray.addObject(indexpath.row)
}

Below code for applying styles on buttons in CellForRowAtIndexPath:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell : CustomCell
    cell = tableView.dequeueReusableCellWithIdentifier("CustomCellID", forIndexPath: indexPath) as! CustomCell
    cell.strName.text = self.names[indexPath.row]
    cell.imgPerson.image = UIImage(named: "\(self.persons[indexPath.row])")!

    if(self.selectedButtonsArray.containsObject(indexpath.row)){

        cell.btnGetUpdates.backgroundColor = UIColor.redColor()
        cell.btnGetUpdates.layer.borderColor = UIColor.darkGrayColor().CGColor
        cell.btnGetUpdates.layer.borderWidth = 1
        cell.btnGetUpdates.layer.cornerRadius = 5.0

    }else{

        cell.btnGetUpdates.layer.borderColor = UIColor.darkGrayColor().CGColor
        cell.btnGetUpdates.layer.borderWidth = 1
        cell.btnGetUpdates.layer.cornerRadius = 5.0

   }

    cell.btnComment.layer.borderColor = UIColor.darkGrayColor().CGColor
    cell.btnComment.layer.borderWidth = 1
    cell.btnComment.layer.cornerRadius = 5.0

    cell.btnReadMore.layer.borderColor = UIColor.darkGrayColor().CGColor
    cell.btnReadMore.layer.borderWidth = 1
    cell.btnReadMore.layer.cornerRadius = 5.0

    cell.dateMissingPersonPlace.text = self.MissingPeoplePlace[indexPath.row]
    cell.dateMissingPersonSince.text = self.MissingPeopleSince[indexPath.row]

    cell.btnGetUpdates.tag = indexPath.row
    cell.btnGetUpdates.addTarget(self, action: "GetUpdatesButton:",forControlEvents: .TouchUpInside)

    return cell

}

I hope this helps to resolve your problem! Thanks.

Asha Dhola
  • 116
  • 2
  • Hi, thank you so much for your answer, I just did this and its working, but...the button, when clicked, is not being changed or updated in instantly, I needed to scroll down and then scroll back up to see the change...any suggestion? – Raymond Led Carasco Sep 14 '15 at 07:28
  • I noticed too that clicking button in CELL 0 , also changes other buttons in other cells as I scroll.... even after putting the if condition in the CellForRow – Raymond Led Carasco Sep 14 '15 at 07:40
  • 1
    Hi my problem is not solved, thanks for answering, it helped me grasp the concept, I used an INT array and saved the indexPath.row there and then compare it in the CellForRowAtIndex – Raymond Led Carasco Sep 14 '15 at 08:16
1

This is because cells are being reused. So when you colour the cells using the button action, the cell's background is being painted which i guess is working fine but the situation is that when you scroll up or down, the previous cell which is having bg colour is being reused and hence you get the previous settings it was.

A tip would be to explicitly set the background colour of the UIButton and UITableViewCell in this method func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell, as everytime it will be reused the colour will be set as default and you will NOT get the previous colour it was being reused from.

Just set

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  cell.backgroundColor = [UIColor whiteColor];
  cell.btnGetUpdates setBackgroundColor:[UIColor whiteColor];
//with along the other code, add these two lines.
}

EDIT

for getting an effect like you want set an instance variable which stores the indexPath of the selected Cell something like an int.

In the button action method store the int to the button.tag property which is also the indexpath.row property of the cell.

in your cellForRowAtIndexPath method, do a check

if(indexpath.row == selectedIntIndexPath){
  //change the colour whatever you want.
}

Finally in the IBAction Method of the button call [self.tableView reloadData]

So the complete code will be something like this

@implementation ViewController{
int selectedIntIndexPath;
}

 -(IBAction)actionForButton:(id)sender{
UIButton *btn = (UIButton *)sender;
selectedIntIndexPath = btn.tag;
[self.customTableView reloadData];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UICustomTableViewCell *cell = (UICustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"reuseIdentifire" forIndexPath:indexPath];
if(indexPath.row == selectedIntIndexPath){
    // do your colour
  }
 return cell;
}
Saheb Roy
  • 5,899
  • 3
  • 23
  • 35
  • Thanks a lot! :) I'll try this one and let you know after a while – Raymond Led Carasco Sep 14 '15 at 06:53
  • I have tried storing the NSIndexPath of the clicked button by getting its TAG (tag is set as indexath.row) in var clickedButton: [NSIndexPath] = [] ,, and I used this inside my CellForRowAtIndex – Raymond Led Carasco Sep 14 '15 at 07:01
  • if(self.clickedButton.contains(indexPath)){ cell.btnGetUpdates.layer.borderColor = UIColor.redColor().CGColor cell.btnGetUpdates.layer.borderWidth = 1 cell.btnGetUpdates.layer.cornerRadius = 5.0 }else{ cell.btnGetUpdates.layer.borderColor = UIColor.redColor().CGColor cell.btnGetUpdates.layer.borderWidth = 1 cell.btnGetUpdates.layer.cornerRadius = 5.0 } – Raymond Led Carasco Sep 14 '15 at 07:02
  • I ended up seeing EVERY button having RED BORDER COLOR ( supposedly, it should only occur when a button is clicked, so I guess its still not working _ – Raymond Led Carasco Sep 14 '15 at 07:02
  • you got to set the background colour to white in `cellForRowAtIndexPath` method – Saheb Roy Sep 14 '15 at 07:07
  • Yes, I already did that, I cant seem to figure out how to have that effect (bordercolor change) in just the cell that I clicked and stays there whether I scroll down or not. – Raymond Led Carasco Sep 14 '15 at 07:09
  • Hi my problem is not solved, thanks for answering, it helped me grasp the concept, I used an INT array and saved the indexPath.row there and then compare it in the CellForRowAtIndex – Raymond Led Carasco Sep 14 '15 at 08:16
1

Inside the GetUpdatesButton() function , keep a flag value for the indexPath.row value in a separate array. Then reload the specific cell of the table view.

Now in the cellForRowAtIndexPath function make a condition of checking the flag value is set or not, If set then change background colour else set the default colour.

PS. there are different kinds of work arounds to solve the issue. Just understand the logic and go for the one which is useful in the code.

Sujith Chandran
  • 2,671
  • 1
  • 15
  • 13
  • Thanks, I understand and have tried doing so by storing the NSINdexpath of the clicked button ( if the button is in row 0, then I store NSIndexPath(0,0) ) but it didn't work... and I still seee the same result as I did for this post.... can you elaborate this? – Raymond Led Carasco Sep 14 '15 at 06:52
  • When I click at CELL 0, I would also see random button from other cells as I scroll down being highlighted too – Raymond Led Carasco Sep 14 '15 at 07:35
  • Hi my problem is not solved, thanks for answering, it helped me grasp the concept, I used an INT array and saved the indexPath.row there and then compare it in the CellForRowAtIndex – Raymond Led Carasco Sep 14 '15 at 08:16
1

Here my solutions, in order to expand @RaymondLedCarasco comment:

Tested in Swift 4 Xcode 9.3

Declare a propierty:

var numberCells: [Int] = []

When click the button of Cell:

self.numberCells.append(indexPath.row)

In CellForRowAtIndex check:

if self.numberCells.contains(indexPath.row) { ... } else { ... }
xhinoda
  • 1,032
  • 1
  • 19
  • 26
0

Here most of TableViewCell problems I have solved, Like when clicking the cell button after scrolling the remaining rows may be changed and when selecting at didSelectRowAt indexPath: IndexPath the row, after scrolling the remaining rows may be selected. So I created an app to solve those problems. Here adding GitHub code Find the code

  • In the first screen, click on a red button and just scroll down you can't see other cells are affected automatically in UITableViewCell.

  • Clicking on didSelectRowAt indexPath: IndexPath in the first screen you can go to details screen that's the second screen, in that screen after selecting a row other rows won't be selected automatically in UITableViewCell.

Sreekanth G
  • 658
  • 6
  • 13