I'm making an inventory app and have subclassed a grouped UITableView that I made the background a UIImage that I set after initialization based in the location (top, middle, bottom, single). Then I set the detail image and text based on a cached array of categories and tools. The problem is that when stress tested (or tested at all) this runs very slow. Can anyone show me why this is running slow and/or give me some pointers to make it faster?
This is what it's supposed to look like:
Here's the code for the subclassed UITableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.backgroundImage = [[[UIImageView alloc] initWithFrame:CGRectMake(-3, 0, 305.0f, 50)] autorelease];
self.roundedImageView = [[[UIImageView alloc] initWithFrame:CGRectMake(10, 8, 33, 33)] autorelease];
self.label = [[[UILabel alloc] initWithFrame:CGRectMake(50, 7, 140, 40)] autorelease];
self.topDes = [[[UILabel alloc] initWithFrame:CGRectMake(215, 5, 100, 15)] autorelease];
self.botDes = [[[UILabel alloc] initWithFrame:CGRectMake(215, 18, 100, 35)] autorelease];
self.midDes = [[[UILabel alloc] initWithFrame:CGRectMake(215.0f, 15.0f, 80.0f, 22.0f)] autorelease];
[self.contentView addSubview:self.backgroundImage];
[self.contentView addSubview:self.roundedImageView];
[self.contentView addSubview:self.label];
[self.contentView addSubview:self.topDes];
[self.contentView addSubview:self.midDes];
[self.contentView addSubview:self.botDes];
//Set properties for labels and images
[self prepareForReuse];
}
return self;
}
Here is the code for making the cells for the tableview
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (searching == NO) {
NSString *CellIdentifier = [NSString stringWithFormat:#"%d, %d", indexPath.section, indexPath.row];
OBcell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[OBcell alloc]
initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]
autorelease];
NSString *CellIdentifier = [NSString stringWithFormat:#"%d, %d", indexPath.section, indexPath.row];
OBcell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[OBcell alloc]
initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]
autorelease];
if (searching == NO) {
if (indexPath.row == 0) {
if ([tableView numberOfRowsInSection:indexPath.section] == 1) {
cell.backgroundImage.image = singleTableImage;
} else {
cell.backgroundImage.image = topTableImage;
}
} else if (indexPath.row < [tableView numberOfRowsInSection:indexPath.section] - 1) {
cell.backgroundImage.image = middleTableImage;
} else {
cell.backgroundImage.image = bottomTableImage;
}
if (type == OBViewTypeTools) {
if ([[[[primaryArray objectAtIndex:indexPath.section] objectAtIndex:5] objectAtIndex:indexPath.row] integerValue] > 0) {
if (indexPath.row == 0) {
if ([tableView numberOfRowsInSection:indexPath.section] == 1) {
cell.backgroundImage.image = singleTableImageRed;
} else {
cell.backgroundImage.image = topTableImageRed;
}
} else if (indexPath.row < [tableView numberOfRowsInSection:indexPath.section] - 1) {
cell.backgroundImage.image = middleTableImageRed;
} else {
cell.backgroundImage.image = bottomTableImageRed;
}
}
}
}
}
//Just pay attention to the cell.roundedImageView mainly, I'm thinking that's the problem unless you guys think it's something else...
switch (type) {
case OBViewTypeTools:;
cell.label.text = (NSString*)[[[primaryArray objectAtIndex:indexPath.section] objectAtIndex:2] objectAtIndex:indexPath.row];
cell.roundedImageView.image = [[[primaryArray objectAtIndex:indexPath.section] objectAtIndex:1] objectAtIndex:indexPath.row];
cell.topDes.text = [NSString stringWithFormat:#"Total: %d", [[[[primaryArray objectAtIndex:indexPath.section] objectAtIndex:3] objectAtIndex:indexPath.row] integerValue]];
cell.topDes.textColor = [UIColor yellowColor];
cell.topDes.font = smallFont;
cell.botDes.text = [NSString stringWithFormat:#"In Use: %d", [[[[primaryArray objectAtIndex:indexPath.section] objectAtIndex:4] objectAtIndex:indexPath.row] integerValue]];
cell.botDes.textColor = [UIColor blueColor];
cell.botDes.font = smallFont;
break;
case OBViewTypeCategories:;
ToolCategory *cat = (ToolCategory*)[primaryArray objectAtIndex:indexPath.row];
cell.label.text = cat.name;
cell.midDes.textColor = [UIColor lightGrayColor];
cell.midDes.font = smallFont;
cell.midDes.text = [NSString stringWithFormat:#"Tools: %d", [[cat tools] count] == 0 ? 0 : [[cat tools] count]];
cell.roundedImageView.image = cat.image;
break;
case OBViewTypeJobs:
cell.label.text = ((Job*)[primaryArray objectAtIndex:indexPath.row]).name;
cell.roundedImageView.image = ((Job*)[primaryArray objectAtIndex:indexPath.row]).image;
cell.midDes.text = [NSString stringWithFormat:#"Tools: %d", [((Job*)[primaryArray objectAtIndex:indexPath.row]).tools count]];
cell.midDes.font = smallFont;
cell.midDes.textColor = [UIColor lightGrayColor];
break;
case OBViewTypeJobTools:
cell.label.text = [((Tool*)[[[((ToolCategory*)[primaryArray objectAtIndex:indexPath.section]) tools] sortedArrayUsingDescriptors:sortDescriptors] objectAtIndex:indexPath.row]) name];
cell.roundedImageView.image = [((Tool*)[[[((ToolCategory*)[primaryArray objectAtIndex:indexPath.section]) tools] sortedArrayUsingDescriptors:sortDescriptors] objectAtIndex:indexPath.row]) image];
cell.midDes.text = [NSString stringWithFormat:#"(%d)", [[((ToolCategory*)[primaryArray objectAtIndex:indexPath.section]) tools] count]];
cell.midDes.font = smallFont;
case OBViewTypeToolJobs:
cell.label.text = [((Job*)[secondaryArray objectAtIndex:indexPath.row]) name];
cell.roundedImageView.image = [((Job*)[secondaryArray objectAtIndex:indexPath.row]) image];
cell.midDes.textColor = [UIColor blueColor];
cell.midDes.text = [NSString stringWithFormat:#"(%d)", [((Job*)[secondaryArray objectAtIndex:indexPath.row]) amountForTool:(Tool*)primaryObject]];
cell.midDes.font = smallFont;
break;
}
cell.clipsToBounds = NO;
cell.layer.masksToBounds = NO;
cell.backgroundColor = [UIColor clearColor];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
There are a number of potential issues going on with your setup including the following:
1) Your cell reuse identifier is very strange
NSString *CellIdentifier = [NSString stringWithFormat:#"%d, %d",
indexPath.section, indexPath.row];
The cell reuse identifer should be a static NSString that is used to deque like cells. Not reusing cells is very costly because new cells have to be instantiated, instead of reused. Looking at your example screenshot, all these cells are virtually the same, so you should use the same reuse identifier:
static NSString *CellIdentifier = #"InventoryItemCell";
2) You are using a lot of alpha transparency in your cells. In Core Animation, it takes extra processing power to blend alpha channels and transparency should be minimized as much as possible, especially in UITableView. You can run the Core Animation Instrument to get a feel for how much alpha is being used in your app by turning on the Color Blended Layers debug option:
This will put on an overlay to show you which parts of your app are being color blended. Green is opaque, red is alpha:
The object of this is to minimize the amount of red (bad!) on the screen as much as possible when animations are occurring. Sometimes, not all of it can be eliminated, due to design requirements, but reducing it as much as possible will produce good results.
3) I am not sure how you are accomplishing the rounded corners on your images, but this is extremely costly to animate in a tableview if you are doing it by rounding the corners on the UIImageView's CALayer. If you are doing this in code, don't. It is much more performant to save off the images with the image already rounded. (But be cognizant, again, of the penalty incurred with alpha blending!)
Related
This has been driving me crazy for the better part of the day.
I've got a UITableView with UIImageViews. These imageviews load a locally saved PNG-file in the cellForRow-function of the tableview, and this works fine except the tableview will stop scrolling for a fraction of a second when a cell with an image in it scrolls into sight so to speak. I've trawled StackOverflow and google for an answer but I've come up short - so any help will be greatly appreciated.
Here is my code for the CellForRow-function:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if([currSection isEqualToString:#"composer"]){
MySlide *s = [slidesArray objectAtIndex:indexPath.row];
cell.textLabel.hidden = YES;
UIImageView *whiteView = [[UIImageView alloc] initWithFrame:CGRectMake((projectsTable.frame.size.width/2)-150, 4, 204.8, 153.6)];
if([s.slideImage isEqualToString:#""] || s.slideImage == nil){
//no custom image in this cell - go with default background image
whiteView.image = [UIImage imageNamed:#"cellback2.png"];
whiteView.backgroundColor = [UIColor whiteColor];
}else{
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
NSData *data = [[NSData alloc] initWithContentsOfFile:s.slideImage];
UIImage *im = [[UIImage alloc] initWithData:data];
whiteView.image = im;
whiteView.image = [self imageWithImage:whiteView.image CovertToSize:CGSizeMake(204.8,153.6)];
whiteView.backgroundColor = [UIColor whiteColor];
}
[cell.contentView addSubview:whiteView];
[whiteView release];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
There are a couple of changes to be made, first off the UIImageViews should be added when the cell is generated, rather than every time tableView:cellForRowAtIndexPath: is hit (as #Vishy suggests). Secondly you should cache the images you are loading from the documents directory ([UIImage imageNamed:] does this automatically for bundle resources).
#interface MyViewController () {
NSMutableDictionary *_imageCache;
}
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
// other viewDidLoad stuff...
_imageCache = [[NSMutableDictionary alloc] init];
}
- (void)viewDidUnload {
[super viewDidUnload];
// other viewDidUnload stuff...
[_imageCache release];
_imageCache = nil;
}
- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *whiteView = [[UIImageView alloc] initWithFrame:CGRectMake((projectsTable.frame.size.width/2)-150, 4, 204.8, 153.6)];
whiteView.tag = 111;
whiteView.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:whiteView];
[whiteView release];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.textLabel.hidden = YES;
}
UIImageView* iView = (UIImageView*) [cell.contentView viewWithTag:111];
if([currSection isEqualToString:#"composer"]) {
MySlide *s = [slidesArray objectAtIndex:indexPath.row];
if([s.slideImage isEqualToString:#""] || s.slideImage == nil) {
//no custom image in this cell - go with default background image
iView.image = [UIImage imageNamed:#"cellback2.png"];
}
else {
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
// use the image path as the cache key
UIImage *theImage = [_imageCache objectForKey:s.slideImage];
if (theImage == nil) {
// load the image and save into the cache
theImage = [UIImage imageWithContentsOfFile:s.slideImage];
theImage = [self imageWithImage:theImage CovertToSize:CGSizeMake(204.8, 153.6)];
[_imageCache setObject:theImage forKey:s.slideImage];
}
iView.image = theImage;
}
}
}
#end
As a general rule, tableView:cellForRowAtIndexPath: is a method you need to get out of fast, so loading images from disk should be avoided wherever possible.
Change the code as per below...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *whiteView = [[UIImageView alloc] initWithFrame:CGRectMake((projectsTable.frame.size.width/2)-150, 4, 204.8, 153.6)];
whiteView.tag = 111;
whiteView.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:whiteView];
[whiteView release];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.textLabel.hidden = YES;
}
UIImageView* iView = (UIImageView*) [cell.contentView viewWithTag:111];
if([currSection isEqualToString:#"composer"])
{
MySlide *s = [slidesArray objectAtIndex:indexPath.row];
if([s.slideImage isEqualToString:#""] || s.slideImage == nil)
{
//no custom image in this cell - go with default background image
iView.image = [UIImage imageNamed:#"cellback2.png"];
}
else
{
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
iView.image = [UIImage imageWithContentsOfFile:s.slideImage];
iView.image = [self imageWithImage:iView.image CovertToSize:CGSizeMake(204.8,153.6)];
}
}
}
There is also a delay the first time each UIImage is displayed. See this post for details on prompting the work at cache time rather than display time:
Setting image property of UIImageView causes major lag
I know that if I have some images and subviews added in customized cell then I have to reuse the cell so that custom control won't appear on other cells but here I have other issue. I just want to have ImageView on first cell of first section so I have used IndexPath.Section==0 and IndexPath.Row==0 condition in following code but the problem is when I scroll table, the other cell will meet this condition and my code will create imageview on that cell as well. I have tried Tagging it and using same tagged cellView but it didn't help either. The cell issue is with disabling user interactions for few cells. Eventually after scrolling it disables user interactions for all cells. Is there anyway to resolve this?
Thanks.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
if(indexPath.section == 0 && indexPath.row == 0) {
UIImageView *imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"me.jpg"]] autorelease];
UIView *cellView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0,320,132)] autorelease];
[imageView setFrame: CGRectMake(10, 10, 54, 54)];
[cellView addSubview:imageView];
cell.backgroundView = cellView;
return cell;
} else if(indexPath.row == 0) {
NSString * title = [NSString string];
switch (indexPath.section) {
case 1:
title = #"Friends";
break;
case 2:
title = #"Accounts";
break;
case 3:
title = #"Stats";
break;
default:
title = nil;
break;
}
cell.textLabel.text = title;
cell.userInteractionEnabled = NO;
return cell;
}
cell.textLabel.text = #"Test";
return cell;
}
[RESOLVED] Correct code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
if(indexPath.section == 0 && indexPath.row == 0) {
UIImageView *imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"me.jpg"]] autorelease];
cell.imageView.image = imageView.image;
cell.textLabel.text = nil;
cell.textLabel.textColor = [UIColor clearColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = YES;
return cell;
} else if(indexPath.row == 0) {
NSString * title = [NSString string];
switch (indexPath.section) {
case 1:
title = #"Friends";
break;
case 2:
title = #"Accounts";
break;
case 3:
title = #"Stats";
break;
default:
title = nil;
break;
}
cell.imageView.image = nil;
cell.textLabel.text = title;
cell.textLabel.textColor = [UIColor redColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = NO;
return cell;
}
cell.imageView.image = nil;
cell.textLabel.text = [cellItems objectAtIndex:(rows+indexPath.row-1)];
cell.textLabel.textColor = [UIColor blueColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = YES;
return cell;
}
[IMPROVED CODE]
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *NormalCellIdentifier = #"NormalCell";
static NSString *TitleCellIdentifier = #"TitleCell";
NSString *neededCellType;
if(indexPath.section == 0 && indexPath.row == 0) {
neededCellType = TitleCellIdentifier;
} else {
neededCellType = NormalCellIdentifier;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:neededCellType];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:neededCellType] autorelease];
//Only add content to cell if it is new
if([neededCellType isEqualToString: TitleCellIdentifier]) {
UIImageView *imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"me.jpg"]] autorelease];
cell.imageView.image = imageView.image;
}
}
if([neededCellType isEqualToString: NormalCellIdentifier]) {
NSString * title;
if(indexPath.row == 0) {
switch (indexPath.section) {
case 1:
title = #"Friends";
break;
case 2:
title = #"Accounts";
break;
case 3:
title = #"Stats";
break;
default:
title = nil;
break;
}
cell.textLabel.text = title;
cell.textLabel.textColor = [UIColor redColor];
cell.userInteractionEnabled = NO;
} else {
cell.userInteractionEnabled = YES;
cell.textLabel.textColor = [UIColor blueColor];
cell.textLabel.text = #"Test";
}
}
return cell;
}
I think your problem is that the reuse of cells makes it so that the cells that aren't being created as new cells have some properties set that you must redefine. For instance, try assigning cell.userInteractionEnabled = YES to all other cases and see what the result is.
The problem is that you are not allowing for the possibility that the cell that was correctly showing the image gets reused later and the image view is still in there.
Here are two solutions:
set the tag value of the image view when you create it, then when you setup the cells, include code to check for and remove the old imageView if necessary.
assign different reuse identifiers to cells that need an image view and those that do not. Then make sure that you are only adding a new image view to cells when they are being created and not when they are being reused.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *NormalCellIdentifier = #"NormalCell";
static NSString *TitleCellIdentifier = #"TitleCell";
NSString *neededCellType;
if(indexPath.section == 0 && indexPath.row == 0) {
neededCellType = TitleCellIdentifier;
} else {
neededCellType = NormalCellIdentifier;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:neededCellType];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:neededCellType] autorelease];
//Only add content to cell if it is new
if([neededCellType isEqualToString: TitleCellIdentifier]) {
UIImageView *imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"me.jpg"]] autorelease];
UIView *cellView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0,320,132)] autorelease];
[imageView setFrame: CGRectMake(10, 10, 54, 54)];
[cellView addSubview:imageView];
cell.backgroundView = cellView;
}
}
if([neededCellType isEqualToString: NormalCellIdentifier]) {
NSString * title;
if(indexPath.row == 0) {
switch (indexPath.section) {
case 1:
title = #"Friends";
break;
case 2:
title = #"Accounts";
break;
case 3:
title = #"Stats";
break;
default:
title = nil;
break;
}
cell.textLabel.text = title;
cell.userInteractionEnabled = NO;
}
else {
cell.textLabel.text = #"Test";
return cell;
}
}
}
(Those last few lines fell out of the code box). That should do it.
I am subclassing uitableviewcell so that i can apply a standard background and text for all my cells, this is my first attempt at this but I have it mostly displaying how i would like. Although i am stuck on one issue. My table has two groups and i would like the first group to have the text centered and i would like to have the second group align to the left. But no such luck at this point.
CustomCell.m
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
NSIndexPath *indexPath = [(UITableView *)self.superview indexPathForCell: self];
NSLog(#"%i", indexPath);
int rows = [(UITableView *)self.superview numberOfRowsInSection:indexPath.section];
NSLog(#"%i", rows);
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
if (centerText) {
cellText = [[[UILabel alloc] initWithFrame:CGRectMake(0, 15, self.bounds.size.width - 10, 30)] autorelease];
cellText.textAlignment = UITextAlignmentCenter;
}else {
cellText = [[[UILabel alloc] initWithFrame:CGRectMake(20, 15, self.bounds.size.width - 10, 30)] autorelease];
cellText.textAlignment = UITextAlignmentLeft;
}
cellText.font = [UIFont boldSystemFontOfSize:16];
cellText.backgroundColor = [UIColor clearColor];
cellText.shadowColor = [UIColor colorWithWhite:1.0 alpha:0.5];
cellText.shadowOffset = CGSizeMake(0,1);
cellText.textColor = [UIColor colorWithRed:0x4c/255.0 green:0x4e/255.0 blue:0x48/255.0 alpha:1.0];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:self.frame];
UIImage* img = [UIImage imageNamed:#"odd_slice.png"];
imgView.image = img;
self.backgroundView = imgView;
[imgView release];
UIImage *accessoryImage = [UIImage imageNamed:#"content_arrow.png"];
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessoryImage];
// accessoryView.image = accessoryImage;
self.accessoryView = accessoryView;
[accessoryView release];
//Selected State
UIImage *selectionBackground = [UIImage imageNamed:#"row_selected.png"];
UIImageView *selectionView = [[UIImageView alloc] initWithFrame:self.frame];
selectionView.image = selectionBackground;
self.selectedBackgroundView = selectionView;
[selectionView release];
//Adds Text
[self addSubview:cellText];
}
return self;
}
TableView.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CoCoachAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSArray *keys = [[appDelegate rowersDataStore] allKeys];
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
switch (indexPath.section) {
case 0:
[cell setCenterText:YES];
break;
case 1:
[cell setCenterText:NO];
break;
default:
break;
}
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
switch (indexPath.section) {
case 0:
[cell.cellText setText:#"Create New Rower"];
break;
case 1:
[cell.cellText setText:[keys objectAtIndex:indexPath.row]];
break;
default:
break;
}
// Set up the cell...
return cell;
}
Anyone have any suggestions?
You're misunderstanding how reusable cells work. When you call dequeueReusableCellWithIdentifier you're getting back a cell that was already allocated. It could have been used for either group in your code.
This is the code that makes no sense:
if (cell == nil) {
switch (indexPath.section) {
case 0:
[cell setCenterText:YES];
break;
case 1:
[cell setCenterText:NO];
break;
default:
break;
}
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
You're fetching a cell. If it doesn't exist, then you set the centering (which does nothing; remember, cell is nil). You then create one.
But what you really mean to do is to set the centering after you've fetched or created the cell. You don't care how you get it; you just want to reconfigure it for the current values.
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
switch (indexPath.section) {
case 0:
[cell setCenterText:YES];
[cell.cellText setText:#"Create New Rower"];
break;
case 1:
[cell setCenterText:NO];
[cell.cellText setText:[keys objectAtIndex:indexPath.row]];
break;
default:
break;
}
I am creating my cells from an array. The last object in my area gets created differently, as I want to add a new UILabel to the contentView of the cell. (So that it shows a total number of records in a UILabel in the last cell).
For some reason, my last cell that adds the UILabel to the contentView keeps getting duplicated. It will show up on other cells, screw up their display, etc. I have a feeling this has to do with cell reuse, but I am not sure how to fix it.
Here is my method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellID = #"cellID";
// Bounds
CGRect bounds = [[UIScreen mainScreen] bounds];
Person *person = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
person = [people objectAtIndex:indexPath.row];
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Check if it is the first cell
if (person == [people lastObject]) {
cell.accessoryType = UITableViewCellAccessoryNone;
cell.userInteractionEnabled = NO;
UILabel *count = [[UILabel alloc] initWithFrame:CGRectMake(-4, 11, bounds.size.width - 10, 20)];
count.autoresizingMask = UIViewAutoresizingFlexibleWidth;
count.textColor = [UIColor darkGrayColor];
count.font = [UIFont systemFontOfSize:16];
count.textAlignment = UITextAlignmentCenter;
count.text = person.nameFirst;
cell.textLabel.text = nil;
cell.detailTextLabel.text = nil;
[cell.contentView addSubview:count];
return cell;
}
}//end
cell.textLabel.text = [NSString stringWithFormat:#"%#, %#", person.nameLast, person.nameFirst];
// Check if they are faculty staff
if ([person.status isEqualToString:#"Staff/Faculty"]) {
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#: %#", person.status, person.department];
} else {
cell.detailTextLabel.text = person.status;
}//end
return cell;
}
Can someone help me understand how I can get this working correctly so that my UILabel doesn't get created on different cells?
I have adjusted your method below. Basically, you need to dequeue cells and perform any cell wide settings (disclosure, etc.) when you create the cell. Any individual cell changes should be done after the cell has been dequeued.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellID = #"cellID";
static NSString *lastCellID = #"lastcellID";
// Bounds
CGRect bounds = [[UIScreen mainScreen] bounds];
Person *person = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
person = [people objectAtIndex:indexPath.row];
}
UITableViewCell *cell;
if (person != [people lastObject]) {
cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [NSString stringWithFormat:#"%#, %#", person.nameLast, person.nameFirst];
// Check if they are faculty staff
if ([person.status isEqualToString:#"Staff/Faculty"]) {
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#: %#", person.status, person.department];
} else {
cell.detailTextLabel.text = person.status;
}//end
} else {
cell = [tableView dequeueReusableCellWithIdentifier:lastCellID];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:lastCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.userInteractionEnabled = NO;
UILabel *count = [[UILabel alloc] initWithFrame:CGRectMake(-4, 11, bounds.size.width - 10, 20)];
count.autoresizingMask = UIViewAutoresizingFlexibleWidth;
count.textColor = [UIColor darkGrayColor];
count.font = [UIFont systemFontOfSize:16];
count.textAlignment = UITextAlignmentCenter;
count.tag = 1;
[cell.contentView addSubview:count];
}
UILabel *count = (UILabel *)[cell viewWithTag:1];
count.text = person.nameFirst;
//cell.textLabel.text = nil;
//cell.detailTextLabel.text = nil;
}//end
return cell;
}
It's probably because the cell will be reused. Use a different reuse identifier for the last cell, say, kLastCellID.
UITableViewCell *cell = nil;
// Check if it is the first cell
if (person == [people lastObject]) {
cell = [tableView dequeueReusableCellWithIdentifier:kLastCellID];
if (!cell) {
//create a last cell, with an UILabel in your content view
}
//update the cell's content
} else {
cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (!cell) {
//create a regular cell
}
//update the cell's content
}
Table view cell management has driving me crazy from past two days. Please check the code below and I will explain you the problem in detail..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *nameLabel,*sugarLabel,*searchNameLabel,*searchSugarLabel;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
CGFloat width = [UIScreen mainScreen].bounds.size.width - 50;
CGFloat height = 20;
CGRect frame = CGRectMake(10.0f, 10.0f, width, height);
if(isSearchOn)
{
searchNameLabel = [[UILabel alloc] initWithFrame:frame];
searchNameLabel.textColor = [UIColor blackColor];
searchNameLabel.backgroundColor = [UIColor clearColor];
searchNameLabel.textAlignment = UITextAlignmentLeft;
searchNameLabel.font = [UIFont systemFontOfSize:14.0f];
searchNameLabel.tag=260;
[cell.contentView addSubview:searchNameLabel];
[searchNameLabel release];
searchSugarLabel= [[UILabel alloc] initWithFrame:frame];
searchSugarLabel.textColor = [UIColor blackColor];
searchSugarLabel.backgroundColor = [UIColor clearColor];
searchSugarLabel.textAlignment = UITextAlignmentLeft;
searchSugarLabel.font = [UIFont systemFontOfSize:14.0f];
searchSugarLabel.tag=160;
[searchSugarLabel setHidden:YES];
[cell.contentView addSubview:searchSugarLabel];
[searchSugarLabel release];
}
else{
nameLabel = [[UILabel alloc] initWithFrame:frame];
nameLabel.textColor = [UIColor blackColor];
nameLabel.backgroundColor = [UIColor clearColor];
nameLabel.textAlignment = UITextAlignmentLeft;
nameLabel.font = [UIFont systemFontOfSize:14.0f];
nameLabel.tag=60;
[cell.contentView addSubview:nameLabel];
[nameLabel release];
sugarLabel= [[UILabel alloc] initWithFrame:frame];
sugarLabel.textColor = [UIColor blackColor];
sugarLabel.backgroundColor = [UIColor clearColor];
sugarLabel.textAlignment = UITextAlignmentLeft;
sugarLabel.font = [UIFont systemFontOfSize:14.0f];
sugarLabel.tag=160;
[sugarLabel setHidden:YES];
[cell.contentView addSubview:sugarLabel];
[sugarLabel release];
}
}
else {
if(isSearchOn)
{
searchNameLabel=(UILabel *)[cell.contentView viewWithTag:260];
searchSugarLabel=(UILabel *)[cell.contentView viewWithTag:160];
}
else{
nameLabel=(UILabel *)[cell.contentView viewWithTag:60];
sugarLabel=(UILabel *)[cell.contentView viewWithTag:160];
}
}
if (isSearchOn) {
cellValue = [searchResult objectAtIndex:indexPath.row];
searchSugarLabel.text=cellValue.sugarId;
NSString *searchText = [NSString stringWithFormat:#"%# %#", cellValue.firstName, cellValue.lastName];
searchNameLabel.text=searchText;
NSLog(#"%#",searchNameLabel.text);
NSLog(#"%#",searchSugarLabel.text);
}
else {
NSString *contact=[contactKeys objectAtIndex:[indexPath section]];
NSArray *contactSection=[contactNames objectForKey:contact];
sugar=[db getSugarId:#"Contacts" bySection:contact andIndex:indexPath.row];
NSString *cellText = [contactSection objectAtIndex:[indexPath row]];
// split the text by the : to get an array containing { "AAA", "BBB" }
NSArray *splitText = [cellText componentsSeparatedByString:#":"];
// form a new string of the form "BBB AAA" by using the individual entries in the array
NSString *contactText = [NSString stringWithFormat:#"%# %#", [splitText objectAtIndex:1], [splitText objectAtIndex:0]];
nameLabel.text = contactText;
sugarLabel.text = sugar;
}
return cell;
}
Contacts is a class which has the properties firstName,lastName and sugar id in it..I am assigning the properties of a contacts class to the variables in the database method and returning an array of contact objects. searchResult is now an array of contact objects.The problem is when I logged the contents on the console the database gets everything in it and returns an array of contacts.The contacts in the searchResult points to different memory locations but when I try to debug the cellForRowAtIndexPath method after it gets 6 contacts..the 7th contact points to the same memory location as the 1st and it repeats thus in the searchNameLabel.text it returns a null and indexPath points to nil...I think it is cell re use issue and I accept that I am very bad in that..I need to figure this out as I am going to finish off my project with this...please guys help me...
This happens only when I try to search for the contacts. It works fine when I try to load all the contacts onto the table..
You are initializing your subviews in the if(cell== nil) block, but in the corresponding else-block, you overwrite them again.
You should rethink your design: Do not load different views by searchon, but set their properties depending on searchon
if(cell == nil){
//do all initializing
}
if(searchon){
//set view/label properties for searching style
} else {
//set view/label properties for not-searching style
}
another approach could be to have totally separated NIB files for the searchon/!searchon
if(searchon){
static NSString *SearchOnCellIdentifier = #"SearchOnCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: SearchOnCellIdentifier];
if (cell == nil){
//load cell from extra nib
}
} else {
static NSString *SearchOFFCellIdentifier = #"SearchOFFCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: SearchOFFCellIdentifier];
if (cell == nil){
//load cell from extra nib
}
}
NOTE: I never did that and it is not tested.