Ian Gray
Ian Gray

Reputation: 387

iOS: Cell content not being cleared with reuseidentifier

I'm trying to build a table with cells that list an array in UILabels. So each cell will have its size and amount of labels determined by the number of objects in that array. However, I'm running into an issue where the content is initially input correctly, but then as the table cells get recycled during the scroll, the content doesn't clear.

Broken UITableView overlapping cell content

Here's my entire implementation. If someone could please give me an explanation as to how I can prevent the labels from being retained during the recycling process I'd really appreciate it. Thank you.

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
    UITableView *tableView;
}

@end

ViewController.m

#import "ViewController.h"
#import "CustomCell.h"

@interface ViewController () {
    NSArray *myTableArray;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Set up View
    self.view = [[UIView alloc] initWithFrame: [UIScreen mainScreen].applicationFrame];
    self.view.backgroundColor = [UIColor whiteColor];
    self.view.clipsToBounds = YES;
    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    // Set up Table
    CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
    tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain];
    tableView.backgroundColor = [UIColor clearColor];
    tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    tableView.dataSource = self;
    tableView.delegate = self;
    [tableView registerClass:[CustomCell class] forCellReuseIdentifier:@"CustomCell"];
    [self.view addSubview:tableView];

    //Set up dummy array for cells (in my actual app I'm pulling from Core Data)
    NSArray *cell1 = [[NSArray alloc]initWithObjects:@"Some Text", nil];
    NSArray *cell2 = [[NSArray alloc]initWithObjects:@"Different", @"Text", nil];
    NSArray *cell3 = [[NSArray alloc]initWithObjects:@"Lorem...", nil];
    NSArray *cell4 = [[NSArray alloc]initWithObjects:@"Thanks", @"for your", @"help", nil];
    NSArray *cell5 = [[NSArray alloc]initWithObjects:@"A bit more", @"text", nil];
    NSArray *cell6 = [[NSArray alloc]initWithObjects:@"Bunch of", @"junk text", nil];
    NSArray *cell7 = [[NSArray alloc]initWithObjects:@"So long", @"and thanks", @"for all", @"the fish", nil];
    NSArray *cell8 = [[NSArray alloc]initWithObjects:@"Ipsum..", nil];
    NSArray *cell9 = [[NSArray alloc]initWithObjects:@"Peter Pan", @"killed", @"The Lost Boys", nil];
    NSArray *cell10 = [[NSArray alloc]initWithObjects:@"All Dogs", @"Go to Heaven", @"Disney", nil];
    myTableArray = [[NSArray alloc]initWithObjects:cell1, cell2, cell3, cell4, cell5, cell6, cell7, cell8, cell9, cell10, nil];
    NSLog(@"%@",myTableArray);

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return myTableArray.count;
}

- (CGFloat)tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    NSArray *arr = [myTableArray objectAtIndex:indexPath.row];
    return 60 * arr.count;
}

- (UITableViewCell*)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"CustomCell";
    CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    if (!cell)
        cell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];

    // Set up the labels
    [cell setLabels:myTableArray[indexPath.row]];

    return cell;
}


@end

CustomCell.h

#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell
- (void)setLabels:(NSArray *)labels;
@end

CustomCell.m

#import "CustomCell.h"

@implementation CustomCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)setLabels:(NSArray *)labels {
    int labelHeight = 60;
    int y = 0;
    for (NSString *string in labels) {
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, y, self.bounds.size.width-20, labelHeight)];
        label.text = string;
        label.font = [label.font fontWithSize:20];
        y += labelHeight;
        [self.contentView addSubview:label];
    }
}
@end

Upvotes: 1

Views: 4685

Answers (3)

L&#233;o Natan
L&#233;o Natan

Reputation: 57050

That's because you are adding a new UILabel subview without removing the old one.

Why are you adding new labels? Use the already existing label. Since you are using UITableViewCellStyleDefault, the cell already contains several labels. Use self.textLabel.text = string instead.

If you wish to keep using a custom label, you should remove all subviews in the cell's prepareForReuse (don't forget to call the super implementation of prepareForReuse). Just remember, that since you have defined the cell as UITableViewCellStyleDefault, it will have other subviews that you should not remove.

Upvotes: 3

nhgrif
nhgrif

Reputation: 62062

In your CustomCell.m:

- (void)prepareForReuse {
    [super prepareForReuse];
    for(UIView *subview in [self.contentView subviews]) {
        [subview removeFromSuperview];
    }
}

prepareForReuse is a method called on cells as the table view is dequeuing them for reuse.

I'll assume that you've minimized what you're actually doing to a very simplistic example, because otherwise, as Leo Natan has suggested, you should simply be using the existing labels.

Upvotes: 10

KudoCC
KudoCC

Reputation: 6952

You should remove all labels before add labels in reused cell.

- (UITableViewCell*)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"CustomCell";
    CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    if (!cell)
        cell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];

    for (UIView * subView in [cell.contentView subviews]) {
        if ([subView isKindOfClass:[UILabel class]]) {
            [subView removeFromSuperview] ;
        }
    }

    // Set up the labels
    [cell setLabels:myTableArray[indexPath.row]];

    return cell;
}

Upvotes: -1

Related Questions