user1493543
user1493543

Reputation: 375

Adding subviews to UITableViewCells causing issue while scrolling

I'm absolutely stumped on how to fix this issue. So I have a UITableView and in the delegate method cellForRowAtIndex: I'm adding several subviews to each cell if the cell is nil (the initial building of the table view). Everything works well and the table view is built, however, when I scroll down a little in the application, the app all of a sudden crashes with SIGBART and gives me the error * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSIndexPath setImage:]: unrecognized selector sent to class 0x3c361e68'** It's weird because I'm not even calling a setImage method anywhere in my code. Here is the code for the delegate method.

- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {


static NSString *CellIdentifier = @"Cell";
UIImageView* imageView;
UILabel* ttitle;
UILabel* ttitle2;
UILabel* ttitle3;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    // Configure cell:
    // *** This section should configure the cell to a state independent of
    //  whatever row or section the cell is in, since it is only executed
    //  once when the cell is first created.


    imageView=[[UIImageView alloc]initWithFrame:CGRectMake(10.0, 11.0, 50.0, 50.0)];
    [imageView setContentMode:UIViewContentModeScaleAspectFill];
    imageView.layer.masksToBounds=YES;
    imageView.layer.cornerRadius=5.0;
    [cell.contentView addSubview:imageView];

    ttitle = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 7.0, 200, 20)] autorelease];
    ttitle.textColor= [UIColor blackColor];
    ttitle.numberOfLines=1;
    ttitle.backgroundColor=[UIColor clearColor];
    ttitle.font=[UIFont fontWithName:@"Arial Bold" size:15.0];
    [cell.contentView addSubview:ttitle];

    if (indexPath.row==0) {

        CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:@"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
        ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.5, 200, size.height)] autorelease];
        ttitle2.textColor= [UIColor darkGrayColor];
        ttitle2.backgroundColor=[UIColor clearColor];
        ttitle2.numberOfLines=0;
        ttitle2.textAlignment = NSTextAlignmentLeft;
        ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle2.font=[UIFont fontWithName:@"Arial" size:14.0];
        [cell.contentView addSubview:ttitle2];

        ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-8.0, 210, 40)] autorelease];
        ttitle3.textColor= [UIColor darkGrayColor];
        ttitle3.backgroundColor=[UIColor clearColor];
        ttitle3.numberOfLines=1;
        ttitle3.textAlignment = NSTextAlignmentLeft;
        ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle3.font=[UIFont fontWithName:@"Arial" size:11.0];
        [cell.contentView addSubview:ttitle3];


    }
    else{

        CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:@"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
        ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.0, 200, size.height)] autorelease];
        ttitle2.textColor= [UIColor darkGrayColor];
        ttitle2.backgroundColor=[UIColor clearColor];
        ttitle2.numberOfLines=0;
        ttitle2.textAlignment = NSTextAlignmentLeft;
        ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle2.font=[UIFont fontWithName:@"Arial" size:14.0];
        [cell.contentView addSubview:ttitle2];


        ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-9.0, 210, 40)] autorelease];
        ttitle3.textColor= [UIColor darkGrayColor];
        ttitle3.backgroundColor=[UIColor clearColor];
        ttitle3.numberOfLines=1;
        ttitle3.textAlignment = NSTextAlignmentLeft;
        ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle3.font=[UIFont fontWithName:@"Arial" size:11.0];
        [cell.contentView addSubview:ttitle3];

    }

}

// Customize cell:
// *** This section should customize the cell depending on what row or section
//  is passed in indexPath, since this is executed every time this delegate method
//  is called.
imageView.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[data objectAtIndex:indexPath.row] valueForKey:@"thumbnail"]]]];

[ttitle setText:[[data objectAtIndex:indexPath.row] valueForKey:@"name"]];
[ttitle2 setText:[[data objectAtIndex:indexPath.row] valueForKey:@"content"]];

NSString* first=[[[data objectAtIndex:indexPath.row] valueForKey:@"hashtag"] stringByAppendingString:@"     "];
NSString* second =[first stringByAppendingString:[[data objectAtIndex:indexPath.row] valueForKey:@"place"]];
NSString* third=[second stringByAppendingString:@"        "];
NSString* fourth=[third stringByAppendingString:@"¤ "];
NSString* conversion=[[[data objectAtIndex:indexPath.row] valueForKey:@"counter"] stringValue];
NSString* fifth=[fourth stringByAppendingString:conversion];
[ttitle3 setText:fifth];





return cell;

}

Appreciate the help guys!

*UPDATED CODE

- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {


static NSString *CellIdentifier = @"Cell";
UIImageView* imageView;
UILabel* ttitle;
UILabel* ttitle2;
UILabel* ttitle3;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    // Configure cell:
    // *** This section should configure the cell to a state independent of
    //  whatever row or section the cell is in, since it is only executed
    //  once when the cell is first created.


    imageView=[[UIImageView alloc]initWithFrame:CGRectMake(10.0, 11.0, 50.0, 50.0)];
    [imageView setContentMode:UIViewContentModeScaleAspectFill];
    imageView.layer.masksToBounds=YES;
    imageView.layer.cornerRadius=5.0;
    imageView.tag=1;
    [cell.contentView addSubview:imageView];

    ttitle = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 7.0, 200, 20)] autorelease];
    ttitle.textColor= [UIColor blackColor];
    ttitle.numberOfLines=1;
    ttitle.tag=69;
    ttitle.backgroundColor=[UIColor clearColor];
    ttitle.font=[UIFont fontWithName:@"Arial Bold" size:15.0];
    [cell.contentView addSubview:ttitle];

    if (indexPath.row==0) {

        CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:@"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
        ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.5, 200, size.height)] autorelease];
        ttitle2.textColor= [UIColor darkGrayColor];
        ttitle2.backgroundColor=[UIColor clearColor];
        ttitle2.numberOfLines=0;
        ttitle2.tag=70;
        ttitle2.textAlignment = NSTextAlignmentLeft;
        ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle2.font=[UIFont fontWithName:@"Arial" size:14.0];
        [cell.contentView addSubview:ttitle2];

        ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-8.0, 210, 40)] autorelease];
        ttitle3.textColor= [UIColor darkGrayColor];
        ttitle3.backgroundColor=[UIColor clearColor];
        ttitle3.numberOfLines=1;
        ttitle3.tag=71;
        ttitle3.textAlignment = NSTextAlignmentLeft;
        ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle3.font=[UIFont fontWithName:@"Arial" size:11.0];
        [cell.contentView addSubview:ttitle3];


    }
    else{

        CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:@"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
        ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.0, 200, size.height)] autorelease];
        ttitle2.textColor= [UIColor darkGrayColor];
        ttitle2.backgroundColor=[UIColor clearColor];
        ttitle2.numberOfLines=0;
        ttitle2.tag=70;
        ttitle2.textAlignment = NSTextAlignmentLeft;
        ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle2.font=[UIFont fontWithName:@"Arial" size:14.0];
        [cell.contentView addSubview:ttitle2];


        ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-9.0, 210, 40)] autorelease];
        ttitle3.textColor= [UIColor darkGrayColor];
        ttitle3.backgroundColor=[UIColor clearColor];
        ttitle3.numberOfLines=1;
        ttitle3.tag=71;
        ttitle3.textAlignment = NSTextAlignmentLeft;
        ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
        ttitle3.font=[UIFont fontWithName:@"Arial" size:11.0];
        [cell.contentView addSubview:ttitle3];

    }

    imageView.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[data objectAtIndex:indexPath.row] valueForKey:@"thumbnail"]]]];

    [ttitle setText:[[data objectAtIndex:indexPath.row] valueForKey:@"name"]];
    [ttitle2 setText:[[data objectAtIndex:indexPath.row] valueForKey:@"content"]];

    NSString* first=[[[data objectAtIndex:indexPath.row] valueForKey:@"hashtag"] stringByAppendingString:@"     "];
    NSString* second =[first stringByAppendingString:[[data objectAtIndex:indexPath.row] valueForKey:@"place"]];
    NSString* third=[second stringByAppendingString:@"        "];
    NSString* fourth=[third stringByAppendingString:@"¤ "];
    NSString* conversion=[[[data objectAtIndex:indexPath.row] valueForKey:@"counter"] stringValue];
    NSString* fifth=[fourth stringByAppendingString:conversion];
    [ttitle3 setText:fifth];


}
else {
    imageView =[cell viewWithTag:1];
    ttitle=[cell viewWithTag:69];
    ttitle2=[cell viewWithTag:70];
    ttitle3=[cell viewWithTag:71];
}

//STUFFOUTSIDE

// Customize cell:
// *** This section should customize the cell depending on what row or section
//  is passed in indexPath, since this is executed every time this delegate method
//  is called.



return cell;

}

Upvotes: 1

Views: 977

Answers (2)

lnafziger
lnafziger

Reputation: 25740

The problem is that your local variables are not being initialized when the cell is re-used. Here's the current flow for imageView:

UIImageView* imageView;

if (cell == nil)
{
    // Create imageView
    imageView=...
}

// If cell is being reused (ie cell is not nil) then imageView is nil at this point.
imageView.image=...

When you are reusing a table view cell, tableView:dequeueReusableCellWithIdentifier: returns an actual cell instead of nil and the initialization of imageView is skipped.

You need to "find" the imageView that is in the reused cell in order to make changes to it.

Therefore, I suggest that you modify your logic like this:

UIImageView* imageView;

if (cell == nil)
{
    // Create imageView
    imageView=...
}
else
{
    imageView = // get a reference to the imageView
}

imageView.image=...

So now, of course, the question is "how?".

A very common way is to set the tag of the view when you create it so that you can easily retrieve it at a later time. You would use this technique like this:

// Use a unique tag number for each subview.
#define MY_IMAGEVIEW_TAG 1000

UIImageView* imageView;

if (cell == nil)
{
    // Create imageView
    imageView=... // Same as before
    imageView.tag = MY_IMAGEVIEW_TAG; 
}
else
{
    // This is a cell that is being re-used and was previously created.
    // Retrieve a reference to the existing image view that is already in the cell
    imageView = [cell viewWithTag:MY_IMAGEVIEW_TAG];
}

// Now imageView is "safe" to use whether it is a new cell or one that is reused!
imageView.image=...

NOTE: If you are doing a lot of this, creating a UITableViewCell subclass that has properties for each of these subviews would make the use of tags and viewWithTag unnecessary, as well as make your code easier to read.

Upvotes: 2

Fabian Kreiser
Fabian Kreiser

Reputation: 8337

@Inafziger has already posted the correct answer to this question, I just want to explain a little bit more in detail why you're seeing this "weird" crash.
I wouldn't recommend the excessive usage of tags, though. It might be a better idea to create a subclass of UITableViewCell.

You're not initializing your imageView and ttitle variables:

UIImageView *imageView; // imageView can point to anything now!
UILabel* ttitle;

Usually, you'd initialize local variables to nil or 0 (whatever makes sense) when you declare them to avoid such dangling pointers.

Because you're reusing your table view cells cell won't always be nil and your if-clause won't be executed:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) { // Won't be executed if the cell could be dequeued!
    ...
    imageView = ...;
}

Hence, if cell can be dequeued, your imageView and ttitle variables have still not been assigned to anything when you use them!

You're then setting the attributes of the views:

imageView.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[data objectAtIndex:indexPath.row] valueForKey:@"thumbnail"]]]];
[ttitle setText:[[data objectAtIndex:indexPath.row] valueForKey:@"name"]];
[ttitle2 setText:[[data objectAtIndex:indexPath.row] valueForKey:@"content"]]

imageView.image = ...; is the same as calling [imageView setImage:...];. You can read more about that here: http://www.cocoacast.com/cocoacast/?q=node/103

And that's when all comes together: imageView isn't initialized and you're calling -setImage: on it. Boom, crash!

In your case imageView points to the NSIndexPath class. This could be anything, though.
Because of that you're effectively calling -setImage: on the NSIndexPath class (equivalent to: +[NSIndexPath setImage:]) and the app crashes with the +[NSIndexPath setImage:]: unrecognized selector sent to class 0x3c361e68 error message.

Upvotes: 1

Related Questions