Mattias Farnemyhr
Mattias Farnemyhr

Reputation: 4238

Dequeueing UIViews withing UITableViewCell

In a normal situation when working with a UITableView I have the standard code for reusing old cells:

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";
    UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }
    return cell;
}

I noticed, however, that in the case when I added subviews to the cell that they weren't deleted and that a new view were added every time. I have an example below that demonstrate it perfectly:

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";

    UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];

    UILabel *label = [[UILabel alloc] init];
    label.text = @"HELLO";
    label.frame = CGRectMake(arc4random() % 50, -1, 286, 45);
    label.backgroundColor = [UIColor clearColor];

    // Add views
    [cell addSubview:label];

    return cell;
}

I need some code that reuses my labels again in the same way the cells are being reused. What should I do?

Thanks

Upvotes: 2

Views: 339

Answers (4)

Jason Fuerstenberg
Jason Fuerstenberg

Reputation: 1351

I use lazy initialization of views within my custom table cell class. It only needs to load views and "addSubview" once.

- (void) lazyInitTitleLabel {
    if (_titleLabel != nil) {
        return;
    }

    _titleLabel = [[UILabel alloc] initWithFrame: CGRectMake(10.0f, 10.0f, 200.0f, 30.0f)];
    // Cell adds the label as a subview...
    [self addSubview: _titleLabel];
}

The only thing you need to be careful about is resetting any content that views display like text in your labels and images in your image views. If you don't old content may get reused along with the recycled table cells.

Good luck!

Upvotes: 0

The iOSDev
The iOSDev

Reputation: 5267

you can use something like in the else part for if(cell == nil)

for (UIView *sub in [cell.contentView subviews]) 
{
            if([UILabel class] == [sub class])
                NSLog(@"%@",[sub class]);
                UILabel *label = (UILabel *)sub;
                //do label coding ie set text etc.
}

Upvotes: 0

zoul
zoul

Reputation: 104065

I need some code that reuses my labels again in the same way the cells are being reused.

No, you need to understand the table view design better. It should be obvious why the views are being added multiple times – reusing a cell means that you take a previous instance of UITableViewCell that’s no longer needed (thus saving a costly allocation of a new object) and reuse this instance for the new cell. But this previous instance already has the label attached to it, so the number of labels grows.

I would subclass UITableViewCell and put the label creation inside the initialization code for this new class. (Or create a UIView subclass and set it as the cell’s contentView, as suggested in this nice table tutorial by Matt Gallagher.) That’s the proper way to encapsulate the view details and hide them from the table data source.

Upvotes: 0

jrturton
jrturton

Reputation: 119242

You must only add the subviews if you are making a new cell. If you are dequeuing, the subview is already present and should not be re-created.

Your method should be:

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *cellIdentifier = @"Cell"; 

    UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:cellIdentifier]; 
    UILabel *label;
    if (cell == nil) 
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 
        label = [[UILabel alloc] init];
        label.tag = 1;
        // Add views 
        [cell addSubview:label];
    }
    else
    {
        // Label will already exist, get a pointer to it
        label = [cell viewWithTag:1];
    }

    // Now set properties on the subview that are unique to each cell
    label.text = @"HELLO"; 
    label.frame = CGRectMake(arc4random() % 50, -1, 286, 45); 
    label.backgroundColor = [UIColor clearColor]; 

    return cell; 
} 

Note how the label is only created when the cell is nil. Otherwise, it is found using the tag.

Upvotes: 4

Related Questions