TableView cell reuse causing the button title to change while scrolling

1.2k views Asked by At

I am having a list displaying a name and a button titled as "Follow". When I tap on the button title should change to "UnFollow". If I tap on the button again the title should again change to "Follow". This is working fine, but when I am scrolling the table the title of the other cells are also changing due to cell reuse.

The code is as follows:

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *CellIdentifier = @"AuthorsListCell";
    AuthorsListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
                                                                     forIndexPath:indexPath];
    dic_eachAuthor = [[_arr_authors objectAtIndex:indexPath.row] mutableCopy];

    cell.lbl_authorName.text = [dic_eachAuthor objectForKey:@"name"];
    cell.btn_followOrUnfollow.tag = indexPath.row;

    if([dic_eachAuthor valueForKey:@"follow"]){
        [cell.btn_followOrUnfollow setTitle:@"UnFollow" forState:UIControlStateNormal];
    }
    else{

        [cell.btn_followOrUnfollow setTitle:@"Follow" forState:UIControlStateNormal];
    }

    // action button method declarations
    [cell.btn_followOrUnfollow addTarget:self action:@selector(followOrUnfollow:) forControlEvents:UIControlEventTouchUpInside];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}


    -(void)followOrUnfollow:(UIButton *)sender
{
    if ([sender.titleLabel.text isEqualToString:@"Follow"]) {
        [sender setTitle:@"UnFollow" forState:UIControlStateNormal];
        [dic_eachAuthor setValue:@"1" forKey:@"follow"];

    }
    else if ([sender.titleLabel.text isEqualToString:@"UnFollow"]) {
        [sender setTitle:@"Follow" forState:UIControlStateNormal];
        [dic_eachAuthor setValue:nil forKey:@"follow"];


    }


}

Please suggest something to prevent the cell reuse.

5

There are 5 answers

0
Kamal Bhardwaj On

You are missing something here, try this :

-(void)followOrUnfollow:(UIButton *)sender
{
    NSDictionary *dict = (NSDictionary *) _arr_authors[sender tag];
    if ([[dict objectForKey:@"name"] isEqualToString:@"Follow"]) 
     {
        [sender setTitle:@"UnFollow" forState:UIControlStateNormal];

     }
     else if ([[dict objectForKey:@"name"] isEqualToString:@"UnFollow"]) 
    {
        [sender setTitle:@"Follow" forState:UIControlStateNormal];
    }
}
0
agy On

You are cells are reused so please implement this method in AuthorsListTableViewCell:

-(void) prepareForReuse{
  [super prepareForReuse];
  [self.btn_followOrUnfollow setTitle:@"Follow" forState:UIControlStateNormal];
}

// if the cell is reusable (has a reuse identifier), this is called just before the cell is returned from the table view method dequeueReusableCellWithIdentifier:. If you override, you MUST call super. And set the correct default value to the cell.

I also recommend to implement this way:

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *CellIdentifier = @"AuthorsListCell";
    AuthorsListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
                                                                     forIndexPath:indexPath];

    cell.lbl_authorName.text = [dic_eachAuthor objectForKey:@"name"];
    cell.btn_followOrUnfollow.tag = indexPath.row;

    [cell.btn_followOrUnfollow setTitle:@"Follow forState:UIControlStateNormal];
    [cell.btn_followOrUnfollow setTitle:@"UnFollow" forState:UIControlStateSelected];

    if([dic_eachAuthor valueForKey:@"follow"]){
        cell.btn_followOrUnfollow.selected = YES;
    } else{
        cell.btn_followorUnfollow.selected = NO;
    }
    // action button method declarations
    [cell.btn_followOrUnfollow addTarget:self action:@selector(followOrUnfollow:) forControlEvents:UIControlEventTouchUpInside];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}


    -(void)followOrUnfollow:(UIButton *)sender
{
    sender.selected = !sender.selected;

    if ([sender.titleLabel.text isEqualToString:@"Follow"]) {
        [dic_eachAuthor setValue:@"1" forKey:@"follow"];
    }
    else if ([sender.titleLabel.text isEqualToString:@"UnFollow"]) {
        [dic_eachAuthor setValue:nil forKey:@"follow"];
    }


}

In AuthorsListTableViewCell

-(void) prepareForReuse{
  [super prepareForReuse];
  self.btn_followOrUnfollow.selected = NO;
}
0
Akshay Karanth On

Add this condition in followOrUnfollow in cellForRowAtIndexPath also

if ([sender.titleLabel.text isEqualToString:@"Follow"]) {
        [sender setTitle:@"UnFollow" forState:UIControlStateNormal];

    }
    else if ([sender.titleLabel.text isEqualToString:@"UnFollow"]) {
        [sender setTitle:@"Follow" forState:UIControlStateNormal];


    }
1
Ruchish Shah On

Store follow/unfollow state information within datasource.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *CellIdentifier = @"CellIdentifier";
    ListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
                                                                     forIndexPath:indexPath];

    cell.lbl_authorName.text = [[_arr_authors objectAtIndex:indexPath.row] objectForKey:@"name"];
    cell.btn_followOrUnfollow.tag = indexPath.row;

    // action button method declarations
    [cell.btn_followOrUnfollow addTarget:self action:@selector(followOrUnfollow:) forControlEvents:UIControlEventTouchUpInside];

    NSString *btnTitle = [[_arr_authors objectAtIndex:indexPath.row] objectForKey:@"userFollowUnfollow"];
     [sender setTitle:btnTitle forState:UIControlStateNormal];

    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

-(void)followOrUnfollow:(UIButton *)sender
{
    if ([sender.titleLabel.text isEqualToString:@"Follow"]) {
        [sender setTitle:@"UnFollow" forState:UIControlStateNormal];

    }
    else if ([sender.titleLabel.text isEqualToString:@"UnFollow"]) {
        [sender setTitle:@"Follow" forState:UIControlStateNormal];
    }

    [[_arr_authors objectAtIndex:sender.tag]  setValue:sender.titleLabel.text forKey:@"userFollowUnfollow"];

}
5
dstudeba On

When you originally populate the table row, you do it from an array here cell.lbl_authorName.text = [[_arr_authors objectAtIndex:indexPath.row] objectForKey:@"name"];. The problem is that you don't populate the follow or unfollow information from this array. So all you are doing is toggling a button and there is no saving of the state of that button. What you need to do is modify the array to have a place to save the follow/unfollow state. Then populate the state in the table cell from this array. Then when you call followOrUnfollow: you need to modify the state in the array.

When the cell gets reused it goes and checks with the original array to populate it, populate the follow from the array and you will be set.

Edited to add some code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *CellIdentifier = @"CellIdentifier";
    ListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier                                                                     forIndexPath:indexPath];

    cell.lbl_authorName.text = [[_arr_authors objectAtIndex:indexPath.row] objectForKey:@"name"];
    cell.btn_followOrUnfollow.tag = indexPath.row;
    if([[_arr_authors objectAtIndex:indexPath.row] valueForKey:@"follow"]){
          [cell.btn_followOrUnfollow setTitle:@"UnFollow" forState:UIControlStateNormal];
    else{
         [cell.btn_followOrUnfollow setTitle:@"Follow" forState:UIControlStateNormal];
    }

    // action button method declarations
    [cell.btn_followOrUnfollow addTarget:self action:@selector(followOrUnfollow:) forControlEvents:UIControlEventTouchUpInside];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

-(void)followOrUnfollow:(UIButton *)sender
{
    if ([sender.titleLabel.text isEqualToString:@"Follow"]) {
        [sender setTitle:@"UnFollow" forState:UIControlStateNormal];
        [[_arr_authors objectAtIndex:sender.tag] setValue:1 forKey:@"follow"]
    }
    else if ([sender.titleLabel.text isEqualToString:@"UnFollow"]) {
        [sender setTitle:@"Follow" forState:UIControlStateNormal];
        [[_arr_authors objectAtIndex:sender.tag] setValue:0 forKey:@"follow"]
    }
}

I am not at my normal machine, so the syntax is probably off, but you should get the idea. Also note you will have to add the follow property to _arr_authors