views:

577

answers:

2

Hi guys,

I have a UITableView that gets populated via CoreData, and have just noticed something strange. I have about 20 rows or so in the UITable, and when I scroll down the table and back up again, the cell's label gets written over the top of existing text, and keeps on doing it each time i go down and up again. My code for the CellForRowAtIndexPath is:

  // Customize the appearance of table view cells.
  - (UITableViewCell *)tableView:(UITableView *)tableView   cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Some constants ---
const NSInteger TOP_LABEL_TAG = 1001;
const NSInteger BOTTOM_LABEL_TAG = 1002;
UILabel *topLabel;
UILabel *bottomLabel;
const CGFloat LABEL_HEIGHT = 20;
//CGRect Imageframe = CGRectMake(7, 5, 35, 35);
//----



static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
}

//custom implementation of UIView for lazy loading of image 
//LazyUIImageView *lazy = [[LazyUIImageView alloc] initWithFrame:Imageframe];


//[cell addSubview:lazy];



Post *post = [fetchedResultsController objectAtIndexPath:indexPath];

NSSet *medias = post.PostMedia;
Media *media = [medias anyObject];
NSSet *thumbs  = media.MediaThumb;
Thumb *thumb = [thumbs anyObject];


UIImage *img = thumb.url;

if (img)
 cell.imageView.image = img;
else 
 cell.imageView.image = post.authorpic;

//The creation of the top label
topLabel =
[[[UILabel alloc]
  initWithFrame:
  CGRectMake(
    35 + 2.0 * cell.indentationWidth,
    0.5 * (tableView.rowHeight - 2 * LABEL_HEIGHT),
    tableView.bounds.size.width -
    45 - 4.0 * cell.indentationWidth
    - 35,
    LABEL_HEIGHT)]
 autorelease];
[cell.contentView addSubview:topLabel];

topLabel.tag = TOP_LABEL_TAG;
topLabel.backgroundColor = [UIColor clearColor];
topLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
//---------------------------------

//The creation of the bottom label
bottomLabel =
[[[UILabel alloc]
  initWithFrame:
  CGRectMake(
    35 + 2.0 * cell.indentationWidth,
    0.5 * (tableView.rowHeight - 2 * LABEL_HEIGHT) + LABEL_HEIGHT,
    tableView.bounds.size.width -
    35 - 4.0 * cell.indentationWidth
    - 35,
    LABEL_HEIGHT)]
 autorelease];
[cell.contentView addSubview:bottomLabel];
//--------------------------------

//
// Configure the properties for the text that are the same on every row
//
bottomLabel.tag = BOTTOM_LABEL_TAG;
bottomLabel.backgroundColor = [UIColor clearColor];
bottomLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
bottomLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
bottomLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize] - 2];

//
// Create a background image view.
//
cell.backgroundView =
[[[UIImageView alloc] init] autorelease];
cell.selectedBackgroundView =
[[[UIImageView alloc] init] autorelease];;


topLabel.text = post.title;
bottomLabel.text = @"put something here";


//
// Set the background and selected background images for the text.
// Since we will round the corners at the top and bottom of sections, we
// need to conditionally choose the images based on the row index and the
// number of rows in the section.
//
UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = [tableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
if (row == 0 && row == sectionRows - 1)  //all
{
 rowBackground = [UIImage imageNamed:@"topAndBottomRow.png"];
 selectionBackground = [UIImage imageNamed:@"topAndBottomRowSelected.png"];
}
else if (row == 0) //top
{
 rowBackground = [UIImage imageNamed:@"topRow.png"];
 selectionBackground = [UIImage imageNamed:@"topRowSelected.png"];
}
else if (row == sectionRows - 1) //bottom
{
 rowBackground = [UIImage imageNamed:@"bottomRow.png"];
 selectionBackground = [UIImage imageNamed:@"bottomRowSelected.png"];
}
else //middle
{
 rowBackground = [UIImage imageNamed:@"middleRow.png"];
 selectionBackground = [UIImage imageNamed:@"middleRowSelected.png"];
}
((UIImageView *)cell.backgroundView).image = rowBackground;
((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;


//[lazy release];

return cell;

}

Thanks for your help in advance!

+3  A: 

It's because the cell is being cached and you keep adding to the content view without removing what has already been added.

You can either use a more specific cell identifier and add to the content view only on cell creation or clear out the content view before adding the labels again.

To clear a cell's subviews you can add this:

if(cell == nil)
{...}
else
{
    UIView* subview;
    while ((subview = [[[cell contentView] subviews] lastObject]) != nil)
      [subview removeFromSuperview];
}

When cell != nil that means it is a cell you've already created and added sub views to, that's why they need to be cleared.

jan
What do you mean by clearing out content view before adding labels again. where would i put that, at the start of this delegate method?
Doron Katz
+1  A: 

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

This line is telling your app to reuse existing offscreen cells if they exist. So basically you are taking an already existing cell and adding a new subview to it in the same place that a subview already exits. You need to clear out the existing subviews before adding your new subview.

Update in answer to your comment:

jan's code will remove the existing subviews. You would add it to your code above as the else clause of your if cell is nil statement.

So if you don't have a reusable cell available (cell == nil), your code will create a new one, else your code will remove the old subviews from the existing cell:

if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
}
else {
    UIView* subview;
    while ((subview = [[[cell contentView] subviews] lastObject]) != nil)
      [subview removeFromSuperview];
}

an alternate, more terse way of achieving the same thing would be:

if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
}
else {
    [[[cell contentView] subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
}
dl
thanks, but where would i do this? would i do cell.subviews = nil? or whats the bets way
Doron Katz
jan's code will remove the existing subviews. You would add it to your code above as the else clause of your if cell is nil statement. I've edited my answer above with the full if/else logic.
dl
Thanks guys, you both did awesome. And more importantly helped me understand the cell == nil concept a bit more.
Doron Katz