timpone
timpone

Reputation: 19969

How to set up this custom UITableViewCell

I have taken over an iOS project and have to refactor a list of views into a UITableView. I am using Storyboards and have subclassed UITableViewCell. One subclass is called MenuItemCell and has a headerLabel, detailLabel, and priceLabel which are properties set up in the Storyboard and configured in MenuItemCell. I am able to manipulate these via cellForAtIndexPath like this:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
  static NSString *MenuItemCellIdentifier=@"MenuItemCell";
  id dic=self.tmpMenu.listItems[indexPath.row];
  if([dic isKindOfClass:[MenuItem class]]){
    MenuItemCell *cell = [self.menuTV dequeueReusableCellWithIdentifier:MenuItemCellIdentifier];
    MenuItem *menuItem=(MenuItem *)dic;
    cell.menuItem=menuItem;
    
    cell.headerLabel.text=menuItem.header;
    cell.headerLabel.numberOfLines=0;

    cell.priceLabel.text=menuItem.price;

     // how to handle this custom spotView
     if([menuItem hasInstoreImage]){
        UIView *instoreImageDot=[self circleWithColor:[UIColor redColor] radius:4];
        [cell.spotView addSubview:instoreImageDot];  // ON SCROLLING, this populates to all the different table cells
      }

    return cell;
  }
  return nil;
}

The last piece is that there is a custom UIView called spotView. Currently, I am creating this circle in code in my controller via circleWithColor and trying to add to [cell.spotView] but scrolling causes this to populate on different table cells. How should I set this up? I have added a method to my custom view but this suffers from the same problem.

Upvotes: 0

Views: 68

Answers (2)

pnavk
pnavk

Reputation: 4632

What is happening is that iOS is reusing cells as you scroll and some of the reused cells already have the instoreImageDot view added as a subview.

You really shouldn't do layout stuff in the cellForRowAtIndexPath method. It should only ever be used to dequeue a reusable cell and then set the data for the cell. All the layout stuff should be handled by the cell itself.

Don't create the instoreImageDot in the controller. Add a method in your custom cell - something like (written in C#, but should be easy to translate):

UpdateCell(MenuItem item, bool hasInstoreIamge)
{
    menuItem = item;
    headerLabel.text = item.header;
    priceLabel.text = item.price;
    headerLabel.numberOfLines=0;

    if (hasInstoreImage)
    {
       // code to add the instoreImageDot as a subview of the cell
    }
}

Also in the Custom Cell, Implement the prepareForReuse method and inside this method, remove the instoreImageDot view from the cell - so that it can only ever be added once.

- (void)prepareForReuse {
    if([self.subviews containsObject:instoreImageDot])
    {
       [instoreImageDot removeFromSuperview];
     }

    [super prepareForReuse]; 
}

Now your cellForRowAtIndexPath method can look like:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
  static NSString *MenuItemCellIdentifier=@"MenuItemCell";
  id dic=self.tmpMenu.listItems[indexPath.row];
  if([dic isKindOfClass:[MenuItem class]]){
    MenuItemCell *cell = [self.menuTV dequeueReusableCellWithIdentifier:MenuItemCellIdentifier];
    MenuItem *menuItem=(MenuItem *)dic;

cell.UpdateCell(menuItem, [menuItem hasInstoreImage]);

    return cell;
  }
  return nil;
}

Upvotes: 1

meda
meda

Reputation: 45500

Cells get reused, you will need to tell the tableView to remove the custom View

 if([menuItem hasInstoreImage]){
    UIView *instoreImageDot=[self circleWithColor:[UIColor redColor] radius:4];
    [cell.spotView addSubview:instoreImageDot]; 
  }else{
    //remove it if condition is not met
    //or You can add a place holder view instead
  }

Upvotes: 2

Related Questions