I have a TableView in which I am adding custom UILabels to the UITableViewCells. The tableView loads fine, but when it tries to dequeue a cell after scrolling the app crashes when it tries to set the text of the UILables. Code follows:
#define STYLE_NUMBER_TAG 0
#define COLORWAY_TAG 1
#define SIZE_TAG 2
#define QUANTITY_TAG 3
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get the managedObject
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
OrderLineItem *item = (OrderLineItem *)managedObject;
static NSString *CellIdentifier = #"lineItemCell";
UILabel *styleNumberLabel, *colorwayLabel, *sizeLabel, *quantityLabel;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
quantityLabel = [[[UILabel alloc] initWithFrame:CGRectMake(5, 5, 70, 20)] autorelease];
quantityLabel.tag = QUANTITY_TAG;
[cell.contentView addSubview:quantityLabel];
styleNumberLabel = [[[UILabel alloc] initWithFrame:CGRectMake(85, 5, 70, 20)] autorelease];
styleNumberLabel.tag = STYLE_NUMBER_TAG;
[cell.contentView addSubview:styleNumberLabel];
colorwayLabel = [[[UILabel alloc] initWithFrame:CGRectMake(165, 5, 70, 20)] autorelease];
colorwayLabel.tag = COLORWAY_TAG;
[cell.contentView addSubview:colorwayLabel];
sizeLabel = [[[UILabel alloc] initWithFrame:CGRectMake(245, 5, 70, 20)] autorelease];
sizeLabel.tag = SIZE_TAG;
[cell.contentView addSubview:sizeLabel];
} else {
styleNumberLabel = (UILabel *)[cell.contentView viewWithTag:STYLE_NUMBER_TAG];
colorwayLabel = (UILabel *)[cell.contentView viewWithTag:COLORWAY_TAG];
sizeLabel = (UILabel *)[cell.contentView viewWithTag:SIZE_TAG];
quantityLabel = (UILabel *)[cell.contentView viewWithTag:QUANTITY_TAG];
}
// Configure the cell...
styleNumberLabel.text = item.style.styleNumber; //CRASHES HERE when dequeueing
colorwayLabel.text = item.colorway;
sizeLabel.text = item.size;
quantityLabel.text = [item.quantity stringValue];
return cell;
}
Thanks
There are two factors at play here:
The default value of a UIView's tag
is 0.
The receiver of viewWithTag: is
included in the search.
Consequently, your call to [cell.contentView viewWithTag:STYLE_NUMBER_TAG] is returning the content view itself when you really want it to return the UILabel.
The solution is simple. Don't use 0 as a tag.
Related
in my app i have an UITableView with 120 rows and every row has 1 UItextfeilds and 1 Buttons as show in the code bellow :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
else
{
}
UITextField *NameTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 10, 230, 28)];
NameTextField.borderStyle = UITextBorderStyleRoundedRect;
NameTextField.delegate = self;
NameTextField.textColor = UIColorFromRGB(0x2A1807);
NameTextField.font = [UIFont fontWithName:#"Helvetica" size:(17.0)];
NameTextField.font = [UIFont boldSystemFontOfSize:20];
NameTextField.tag = [indexPath row ];
NSString *temp = [self.sectionNames objectAtIndex:[indexPath section]];
NameTextField.text = [[self.myDataArray objectForKey:temp] objectAtIndex:[indexPath row]];
[cell.contentView addSubview:NameTextField];
[NameTextField release];
UIButton * aBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *wijzigImage = [UIImage imageNamed:#"btn_delete.png"];
aBtn.frame = CGRectMake(240, 10, 28, 26);
[aBtn setImage:wijzigImage forState:UIControlStateNormal];
[aBtn addTarget:self action:#selector(deleteCustomCellWithUIButton:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:aBtn];
return cell;
}
i have noticed that the Scrolling is slow and It doesn't go fluently.
any idea ?
Thanks
That's because you create textfields and buttons every time, add it inside if (cell == nil) {...}. The only thing that should be left outside that if is textField text setting.
Btw, your textfield leaks.
I found the solution :
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
else
{
UITextField *oldTextField = (UITextField *)[cell.contentView viewWithTag:999];
[oldTextField removeFromSuperview];
UIButton *oldBtn = (UIButton *)[cell.contentView viewWithTag:888];
[oldBtn removeFromSuperview];
}
NameTextField.tag = 999;
aBtn.tag = 88;
I put multiple UILabels inside every cell in a UITableView instead of a single cell.textLabel.text. I then use reloaddata to put new uilabels. How do i get rid of the old labels ?
edit: If i put 5 labels in a cell then reload the cell using only 2 labels, there are 3 more labels left over from the last time i called cellForRowAtIndexPath.
If i use viewWithTag like Goldeen said, i can reuse old labels but can i remove labels i dont want from memory ?
edit:
this is my method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
MyTableCell *cell = (MyTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[MyTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(j*50.0, 0, 49.0,logicTable.rowHeight)] autorelease];
label.tag = 1;
label.text = [NSString stringWithFormat:#"ABC"];
label.textAlignment = UITextAlignmentCenter;
label.autoresizingMask = UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:label];
return cell;
}
What it sounds like you are doing is, in your cellForRowAtIndexPath method, you are setting up your UITableViewCells with some labels in them and each time, you are making the labels from scratch. What you should be doing is, setting up the labels if you are making a new cell, and then setting the values on the labels outside of this to fully utilize the ability to reuse table view cells to improve performance of scrolling the table view.
The key method is -viewWithTag: which, along with the tag property on UIView you can use to find a specific subview.
A little sample code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCellIdentifier";
UITableViewCell *cell = (WHArticleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *firstLabel = nil;
UILabel *secondLabel = nil;
UILabel *thirdLabel = nil;
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
firstLabel = [[[UILabel alloc] initWithFrame: CGRectMake(0.0, 0.0, 20.0, 20.0)] autorelease];
firstLabel.tag = 1;
[cell addSubview:firstLabel];
secondLabel = [[[UILabel alloc] initWithFrame: CGRectMake(20.0, 0.0, 20.0, 20.0)] autorelease];
secondLabel.tag = 2;
[cell addSubview:secondLabel];
thirdLabel = [[[UILabel alloc] initWithFrame: CGRectMake(40.0, 0.0, 20.0, 20.0)] autorelease];
thirdLabel.tag = 3;
[cell addSubview:thirdLabel];
}
else
{
firstLabel = (UILabel *)[cell viewWithTag:1];
secondLabel = (UILabel *)[cell viewWithTag:2];
thirdLabel = (UILabel *)[cell viewWithTag:3];
}
firstLabel.text = #"First Label's Text Here";
secondLabel.text = #"Second Label's Text Here";
thirdLabel.text = #"Third Label's Text Here";
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
Book *aBook = [appDelegate.books objectAtIndex:indexPath.row];
UILabel *myLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, 300, 22)];
UILabel *myLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(0, 40, 300, 22)];
UILabel *myLabel3 = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, 300, 22)];
myLabel1.text=aBook.title;
myLabel2.text=aBook.description;
myLabel3.text=aBook.pubDate;
[cell addSubview:myLabel1];
[cell addSubview:myLabel2];
[cell addSubview:myLabel3];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Set up the cell
return cell;
}
I am having this code. It displays from XML file. When I scroll, the text gets overlapped. Please help me out.
You add labels to your cell each time cell is reused so you end with multiple labels stacked on each other in one cell. What you need to change is to create labels only when cell itself is being created:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
UILabel *myLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, 300, 22)];
UILabel *myLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(0, 40, 300, 22)];
UILabel *myLabel3 = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, 300, 22)];
myLabel1.tag = 101;
myLabel2.tag = 102;
myLabel3.tag = 103;
[cell.contentView addSubview:myLabel1];
[cell.contentView addSubview:myLabel2];
[cell.contentView addSubview:myLabel3];
[myLabel1 release];
[myLabel2 release];
[myLabel3 release];
}
// Configure the cell.
Book *aBook = [appDelegate.books objectAtIndex:indexPath.row];
UILabel *myLabel1 = (UILabel*)[cell.contentView viewWithTag:101];
UILabel *myLabel2 = (UILabel*)[cell.contentView viewWithTag:101];
UILabel *myLabel3 = (UILabel*)[cell.contentView viewWithTag:101];
myLabel1.text=aBook.title;
myLabel2.text=aBook.description;
myLabel3.text=aBook.pubDate;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Set up the cell
return cell;
}
Two more things to fix:
Do not forget to release labels you create after adding them to the cell, otherwise you get memory leak and may eventually run into low memory problems (especially with tableview)
add subviews to cell's contentView, not to the cell directly
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#""];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.backgroundView = [[[CustomCell alloc] init] autorelease];
cell.selectedBackgroundView = [[[CustomCell alloc] init] autorelease];
// At end of function, right before return cell:
cell.textLabel.backgroundColor = [UIColor clearColor];
// Configure the cell.
UILabel *myLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 45)];
UILabel *myLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(5, 55, 300, 20)];
UILabel *myLabel3 = [[UILabel alloc] initWithFrame:CGRectMake(0, 68, 300, 60)];
Book *aBook = [appDelegate.books objectAtIndex:indexPath.row];
myLabel1.text=aBook.title;
myLabel2.text=aBook.pubDate;
myLabel3.text=aBook.description;
//myLabel1.lineBreakMode=UILineBreakModeCharacterWrap;
myLabel1.lineBreakMode=UILineBreakModeWordWrap;
myLabel1.numberOfLines=1;
myLabel1.textColor=[UIColor redColor];
myLabel1.backgroundColor = [UIColor blueColor];
myLabel1.font=[UIFont systemFontOfSize:14];
myLabel2.font=[UIFont systemFontOfSize:12];
myLabel3.textAlignment=UITextAlignmentLeft;
myLabel3.textColor=[UIColor blueColor];
myLabel3.lineBreakMode=UILineBreakModeCharacterWrap;
myLabel3.numberOfLines=3;
//myLabel3.lineBreakMode=UILineBreakModeWordWrap;
myLabel3.lineBreakMode=UILineBreakModeTailTruncation;
myLabel3.font=[UIFont systemFontOfSize:14];
//myLabel1.shadowColor=[UIColor redColor];
//myLabel1.backgroundColor=[UIColor grayColor];
[cell.contentView addSubview:myLabel1];
[cell.contentView addSubview:myLabel2];
[cell.contentView addSubview:myLabel3];
//cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[myLabel1 release];
[myLabel2 release];
[myLabel3 release];
//Set up the cell
return cell;
guys i have these codings. in mylabel 1 if i sets to 2, the text goes down so i cant see.
now finally i want to display 2lines of Title, 1 line of pubDate and 3 Lines of Description in one row.
i have displayed but i need some alignment i.e. above points, it should remove HTML(&mdash) tags
i dont know hw to customizing this.struggling with this.pls help me out
Subclass the UITableViewCell and use an that custom cell.
First problem here is that you add the labels each time your cell is displayed. But you may reuse some cells that all ready have in their contentView the labels so you can reuse these labels.
Second problem is the performance each time you alloc init and release 3 labels on each row is displayed. This will produce slow scroll speed on slow devices like iPhone 3G.
Take a look at apple's CustomTableViewCell sample code.
Here is an tutorial how to subClass UITableViewCell
With an custom cell class your method will Look like this
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CustomTableViewCell *cell = (CustomTableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell setLabel1String:aBook.title];
[cell setLabel2String:aBook.pubDate];
[cell setLabel3String:aBook.description];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
I'm not sure if this is the correct way to create a UITableViewCell in code, but it works perfectly, until I need to reload the table as its returning old cells so not loading the new content.
- (UITableViewCell *)tableView:(UITableView *)tableViewData cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableViewData dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
if (indexPath.section == 0) {
if ([marker objectForKey:#"imageUrl"]) {
UIView *transparentBackground = [[UIView alloc] initWithFrame:CGRectZero];
transparentBackground.backgroundColor = [UIColor clearColor];
cell.backgroundView = transparentBackground;
UIImageView *buildingView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 10, 100, 100)];
buildingView.image = [self imageWithImage:[ImageManipulator makeRoundCornerImage:[self loadImage:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.qut.edu.au%#", [marker objectForKey:#"imageUrl"]]]] :9 :9]];
[cell addSubview:buildingView];
} else {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 200, 15)];
label.text = #"test";
[cell addSubview:label];
[label release];
}
} else {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 200, 15)];
label.text = #"test";
[cell addSubview:label];
[label release];
}
}
return cell;
}
Basically the cell that gets returned on line 3 is not nil (its the old one), therefore the if statement isn't fired and it returns the old cell. I could load a new cell each time but that will have issues with load and memory usage.
Whats the correct way to do this?
It will be the old one as it's only (hopefully) created once, that's the point of queueing and dequeueing the reusable cell, to save memory. You'll have to just clear out any of the properties you need to.