Objective-C: UITableView cells get written over each time when scrolling up and down
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开发者_JAVA技巧 == 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!
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)];
}
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.
When ever we use reuse identifier for UITableView,it understands that the cell needs to be reused and the values in the cells overwritten with one another when we are not specifying index values of the cell identifier to be reused.
We need to specifically tell the table view cell,to reuse the cell at index path,i.e.
NSString *cellIdentifier = [NSString stringWithFormat:@"S%1dR%1d",indexPath.section,indexPath.row];
and use this cell identifier in if(cell == nil)
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellIdentifier:cellidentifier];
This would eliminate the mis communication among cells and values in cells doesn't get over written with one another.More over this avoids the hassle of communication between index locations or cell index values.Hope this helps,thanks :)
Dont want to autorelease the UITableViewCell because your app may crash when scrolling multiple times and change the position of device .
You can remove the subview in else condition when cell is not nil.
static NSString *CellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//create a uitableviewcell
if(cell == nil)
{
//Do whatever you want to do
}else{
[[cell.contentView viewWithTag:YOUR_TAG] removeFromSuperview];
}
精彩评论