Thursday, June 23, 2016

UITableViewCell prepareForReuse


In my UITableView dequeueReusableCellWithIdentifier post, I have explained the importance of using dequeueReusableCellWithIdentifier for improving UITableView smooth scrolling and performance.

As per that post, Cells will get reused instead of getting allocated and initialized for the remaining cells. In this post, Let's see the precaution we need to take some times when we are going for reuse of cells.

Let's take an example of sample chat application, where sent message should be right aligned and received message should be left aligned to the cell as shown below.


-- ME --
-- FRIEND --
-- ME --
-- FRIEND --
-- ME --


I am subclassing my cell as MyTableViewCell,


  @interface MyTableViewCell : UITableViewCell{
    
  }

  @end

  #import "MyTableViewCell.h"

  @implementation MyTableViewCell

  - (void)awakeFromNib {
      // Initialization code
  }

  - (void)setSelected:(BOOL)selected animated:(BOOL)animated {

      [super setSelected:selected animated:animated];

      // Configure the view for the selected state
  }

  @end



So based on the chat messages response, We need to display the user messages and user friend's messages.


  - (UITableViewCell *)tableView:(UITableView *)tableView 
                                        cellForRowAtIndexPath:  (NSIndexPath *)indexPath
  {
    
      static NSString *CellIdentifier = @"Messages";
     
      MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                                                                                                CellIdentifier];
    
      if(cell == nil){
        
          cell = [[MyTableViewCell allocinitWithStyle:UITableViewCellStyleValue1
                                                                          reuseIdentifier:@"Messages"];

          [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
        
          [cell setBackgroundColor:[UIColor clearColor]];
        
      }
    
      if((indexPath.row%2) == 0){
          cell.detailTextLabel.text = @"-- Me --";
      }
    
      else{
          cell.textLabel.text = @"-- Friend --";
      }
    
      [cell.textLabel setTextColor:[UIColor blackColor]];
    
      return cell;    

  }


As per above delegate method for UITableViewCell, I have hardcoded like, even row numbers will display user's messages and odd row numbers will display user friend's messages.

As per this hardcode, the out put will be like as shown below.


-- ME --
-- FRIEND --
-- ME --
-- FRIEND --
-- ME --


But, Let's see what happens when we start scrolling the table view couple of times.


-- ME --
-- FRIEND --
-- ME --
-- FRIEND --                                  -- ME --
-- ME --


Our output is not as we expected. What happened here is,

If 2nd row is getting reused for 5th row, 2nd row displays user's message and 5th row displays friend's message. As the 2nd row cell got reused for 5th row cell, 5th row cell is displaying both the messages.

These scenarios we should take while using dequeueReusableCellWithIdentifier

Let's see how to solve this.


                         



We can use the prepareForReuse method of UITableViewCell which gets called just before the cell is getting reused.

Add this in MyTableViewCell.m

  -(void)prepareForReuse
  {
      [super prepareForReuse];
    
      self.detailTextLabel.text = @"";
      self.textLabel.text = @"";
  }



As I am making both the label values to empty (resetting cell before reusing), scrolling table view couple of times doesn't create any mess. Your output will be as expected.


So, Resetting the cell in prepareForReuse solves the content mess while scrolling tableview. What If we are not subclassing UITableViewCell. In this case, We can simply reset the cell before assigning the values in cellforRowAtIndexPath delegate method.


   
- (UITableViewCell *)tableView:(UITableView *)tableView 
                                          cellForRowAtIndexPath:  (NSIndexPath *)indexPath
  {
    
    static NSString *CellIdentifier = @"Messages";
    
    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                                                                                                CellIdentifier];
    
    if(cell == nil){
        
        cell = [[MyTableViewCell allocinitWithStyle:UITableViewCellStyleValue1 
                                                                             reuseIdentifier:@"Messages"];

        [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
        
        [cell setBackgroundColor:[UIColor clearColor]];
        
    }
    
    
    cell.textLabel.text = @"";
    cell.detailTextLabel.text = @"";
    
    if((indexPath.row%2) == 0){
        cell.detailTextLabel.text = @"-- Me --";
    }
    
    else{
        cell.textLabel.text = @"-- Friend --";
    }
    
    [cell.textLabel setTextColor:[UIColor blackColor]];
    
    return cell;
    
  }



Hope this post is useful. Feel free to comment in case of any queries.