UITableView repeating cells on scroll
When i scroll my UITableView, for some reason cells seem to be drawn over each other. If i load up my app, a cell will appear something like this:
And upon scrolling this cell off and back on the screen a number of times, it'll start to appear like this:
As you can see, something i can't seem to work out is going wrong. Any ideas?
EDIT: cellForRowAtIndexPath
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:@"%@/%@",
vaultsPath,
[self.vaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
return cell;
AHCellCreation +createCellWithDictionary:Cell:
//General cell design, same every time
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(0, 0, 320, 82);
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithHue:0 saturation:0 brightness:0.91 alpha:1] CGColor], (id)[[UIColor colorWithHue:0 saturation:0 brightness:0.85 alpha:1] CGColor], nil];
[cell.contentView.layer addSublayer:gradient];
UIView *topLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 1)];
topLine.backgroundColor = [UIColor colorWithHue:0 saturation:0 brightness:0.97 alpha:1.0];
[cell addSubview:topLine];
UIView *bottomLine = [[UIView alloc] initWithFrame:CGRectMake(0, 81, 320, 1)];
bottomLine.backgroundColor = [UIColor colorWithHue:0 saturation:0 brightness:0.64 alpha:1.0];
[cell addSubview:bottomLine];
//Preview Image
NSString *previewImageFilePath = [dictionary objectForKey:@"PreviewImage"];
UIImageView *previewImageView = [[UIImageView alloc] initWithFrame:CGRectMake(9, 9, 64, 64)];
previewImageView.image = [UIImage imageWithContentsOfFile:previewImageFilePath];
[cell addSubview:previewImageView];
//Creation date
UILabel *createdOnLabel = [[UILabel alloc] init];
createdOnLabel.frame = CGRectMake(85, -5, 303, 41);
createdOnLabel.text = @"Created on";
createdOnLabel.backgroundColor = [UIColor clearColor];
createdOnLabel.textAlignment = UITextAlignmentLeft;
createdOnLabel.font = [UIFont systemFontOfSize:12];
createdOnLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:createdOnLabel];
NSDate *creationDate = [dictionary objectForKey:@"CreationDate"];
UILabel *creationDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(85, 0, 303, 82)];
creationDateLabel.text = [AHCellCreation createReadableDateFromDate:creationDate];
creationDateLabel.backgroundColor = [UIColor clearColor];
creationDateLabel.textAlignment = UITextAlignmentLeft;
creationDateLabel.font = [UIFont boldSystemFontOfSize:28];
creationDateLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:creationDateLabel];
//Opening date
NSDate *notificationDate = [dictionary objectForKey:@"NotificationDate"];
NSDate *earliest = [notificationDate earlierDate:[NSDate date]];
BOOL notificationPassed;
if (earliest == [NSDate date]) {
notificationPassed = YES;
}
else {
notificationPassed = NO;
}
UILabel *notificationDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(85, 47, 303, 41)];
if (notificationPassed == NO) {
notificationDateLabel.text = @"To be opened";
}
else {
notificationDateLabel.text = @"Opened on"开发者_如何学编程;
}
notificationDateLabel.backgroundColor = [UIColor clearColor];
notificationDateLabel.textAlignment = UITextAlignmentLeft;
notificationDateLabel.font = [UIFont systemFontOfSize:12];
notificationDateLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:notificationDateLabel];
UILabel *notificationDateLabel2 = [[UILabel alloc] init];
notificationDateLabel2.frame = CGRectMake(164, 47, 303, 41);
notificationDateLabel2.text = [AHCellCreation createReadableDateFromDate:notificationDate];
notificationDateLabel2.backgroundColor = [UIColor clearColor];
notificationDateLabel2.textAlignment = UITextAlignmentLeft;
notificationDateLabel2.font = [UIFont boldSystemFontOfSize:12];
notificationDateLabel2.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:notificationDateLabel2];
return cell;
you're just adding more and more UILabels etc to your cell each time it's displayed!
You need to keep track of all the things that you are adding to your cell to make sure that you're only adding them once! There's a couple of ways of doing this but I recommend subclassing UITableViewCell yourself
For example, here's the code to get the createdOnLabel working correctly :
@interface AHTableViewCell : UITAbleViewCell {
UILabel *createdOnLabel;
}
@end
@implementation AHTableViewCell
@end
Then, your cellForRowAtIndexPath code becomes
AHTableViewCell *cell = (AHTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[AHTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
and your create code becomes :
//Creation date
UILabel *createdOnLabel = [cell createdOnLabel];
if (nil == createdOnLabel) {
createdOnLabel = [[UILabel alloc] init];
createdOnLabel.frame = CGRectMake(85, -5, 303, 41);
createdOnLabel.backgroundColor = [UIColor clearColor];
createdOnLabel.textAlignment = UITextAlignmentLeft;
createdOnLabel.font = [UIFont systemFontOfSize:12];
createdOnLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:createdOnLabel];
[cell setCreatedOnLabel:createdOnLabel];
}
createdOnLabel.text = @"Created on";
so, the first time you create the cell, you create the label. All the other times that you ask the cell to be created, you check to see if the label is already there and if it is, just update it's text.
I guess u are keep on adding some views to the cell's content view. The solution can be found only after seeing the code.
Edit:
Actually I have given the solution for the code u have posted. But I would also recommend subclassing UITableViewCell as suggested by deanWombourne.
Yes, my guess is correct.
First, Make the cellForRowAtIndexPath as follows.
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:@"%@/%@",
vaultsPath,
[self.vaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
}
else
{
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:@"%@/%@",
vaultsPath,
[self.vaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell = [AHCellCreation updateCellWithDictionary:dictionary Cell:cell];
}
return cell;
AHCellCreation +createCellWithDictionary:Cell:
//General cell design, same every time
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(0, 0, 320, 82);
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithHue:0 saturation:0 brightness:0.91 alpha:1] CGColor], (id)[[UIColor colorWithHue:0 saturation:0 brightness:0.85 alpha:1] CGColor], nil];
[cell.contentView.layer addSublayer:gradient];
UIView *topLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 1)];
topLine.tag = 100;
topLine.backgroundColor = [UIColor colorWithHue:0 saturation:0 brightness:0.97 alpha:1.0];
[cell addSubview:topLine];
UIView *bottomLine = [[UIView alloc] initWithFrame:CGRectMake(0, 81, 320, 1)];
bottomLine.tag = 101;
bottomLine.backgroundColor = [UIColor colorWithHue:0 saturation:0 brightness:0.64 alpha:1.0];
[cell addSubview:bottomLine];
//Preview Image
NSString *previewImageFilePath = [dictionary objectForKey:@"PreviewImage"];
UIImageView *previewImageView = [[UIImageView alloc] initWithFrame:CGRectMake(9, 9, 64, 64)];
previewImageView.tag = 102;
previewImageView.image = [UIImage imageWithContentsOfFile:previewImageFilePath];
[cell addSubview:previewImageView];
//Creation date
UILabel *createdOnLabel = [[UILabel alloc] init];
createdOnLabel.tag = 103;
createdOnLabel.frame = CGRectMake(85, -5, 303, 41);
createdOnLabel.text = @"Created on";
createdOnLabel.backgroundColor = [UIColor clearColor];
createdOnLabel.textAlignment = UITextAlignmentLeft;
createdOnLabel.font = [UIFont systemFontOfSize:12];
createdOnLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:createdOnLabel];
NSDate *creationDate = [dictionary objectForKey:@"CreationDate"];
UILabel *creationDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(85, 0, 303, 82)];
creationDateLabel.tag = 104;
creationDateLabel.text = [AHCellCreation createReadableDateFromDate:creationDate];
creationDateLabel.backgroundColor = [UIColor clearColor];
creationDateLabel.textAlignment = UITextAlignmentLeft;
creationDateLabel.font = [UIFont boldSystemFontOfSize:28];
creationDateLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:creationDateLabel];
//Opening date
NSDate *notificationDate = [dictionary objectForKey:@"NotificationDate"];
NSDate *earliest = [notificationDate earlierDate:[NSDate date]];
BOOL notificationPassed;
if (earliest == [NSDate date]) {
notificationPassed = YES;
}
else {
notificationPassed = NO;
}
UILabel *notificationDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(85, 47, 303, 41)];
notificationDateLabel.tag = 105;
if (notificationPassed == NO) {
notificationDateLabel.text = @"To be opened";
}
else {
notificationDateLabel.text = @"Opened on";
}
notificationDateLabel.backgroundColor = [UIColor clearColor];
notificationDateLabel.textAlignment = UITextAlignmentLeft;
notificationDateLabel.font = [UIFont systemFontOfSize:12];
notificationDateLabel.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:notificationDateLabel];
UILabel *notificationDateLabel2 = [[UILabel alloc] init];
notificationDateLabel.tag = 106;
notificationDateLabel2.frame = CGRectMake(164, 47, 303, 41);
notificationDateLabel2.text = [AHCellCreation createReadableDateFromDate:notificationDate];
notificationDateLabel2.backgroundColor = [UIColor clearColor];
notificationDateLabel2.textAlignment = UITextAlignmentLeft;
notificationDateLabel2.font = [UIFont boldSystemFontOfSize:12];
notificationDateLabel2.textColor = [UIColor colorWithHue:0.59 saturation:0.29 brightness:0.47 alpha:1.0];
[cell addSubview:notificationDateLabel2];
return cell;
AHCellCreation +updateCellWithDictionary:Cell:
UIView *topLine = (UIView*)[cell viewWithTag:100];
topLine.backgroundColor = [UIColor colorWithHue:0 saturation:0 brightness:0.97 alpha:1.0];
UIView *bottomLine = (UIView*)[cell viewWithTag:101];
bottomLine.backgroundColor = [UIColor colorWithHue:0 saturation:0 brightness:0.64 alpha:1.0];
//Preview Image
NSString *previewImageFilePath = [dictionary objectForKey:@"PreviewImage"];
UIImageView *previewImageView = (UIImageView*)[cell viewWithTag:102];
previewImageView.image = [UIImage imageWithContentsOfFile:previewImageFilePath];
//Creation date
UILabel *createdOnLabel = (UILabel*)[cell viewWithTag:103];
createdOnLabel.text = @"Created on";
NSDate *creationDate = [dictionary objectForKey:@"CreationDate"];
UILabel *creationDateLabel = (UILabel*)[cell viewWithTag:104];
creationDateLabel.text = [AHCellCreation createReadableDateFromDate:creationDate];
//Opening date
NSDate *notificationDate = [dictionary objectForKey:@"NotificationDate"];
NSDate *earliest = [notificationDate earlierDate:[NSDate date]];
BOOL notificationPassed;
if (earliest == [NSDate date]) {
notificationPassed = YES;
}
else {
notificationPassed = NO;
}
UILabel *notificationDateLabel = (UILabel*)[cell viewWithTag:105];
if (notificationPassed == NO) {
notificationDateLabel.text = @"To be opened";
}
else {
notificationDateLabel.text = @"Opened on";
}
UILabel *notificationDateLabel2 = (UILabel*)[cell viewWithTag:106];
notificationDateLabel2.text = [AHCellCreation createReadableDateFromDate:notificationDate];
return cell;
@Gomathi's intuition was correct. You're fetching a recycled cell here:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
This cell already has all the views in it. You're then adding the views again. When you get back a cell from dequeueReusableCellWithIdentifier:
you should reconfigure it (change the values of the text and image fields), not rebuild it from scratch. That's the whole point of reusable cells. Be sure to read the Table View Programming Guide for full details.
The point of cell reuse is to allocate any views in your UITableViewCell only when a cell is not returned from dequeueReusableCellWithIdentifier
. The allocs are what slows down the scrolling in a tableView that should be scrolling smoothly. What you basically need to do, is setup all the views you will need in a cell when you don't get one when dequeuing. When you DO receive a dequeued cell, you should only be setting state on it from some model object or something similar.
精彩评论