Douglas
Douglas

Reputation: 2524

Table View troubles with dequeue cell

So I am having trouble showing my data in a UITableView. I do believe it has something to do with reusing the cells. I have checked online and here at SO but have not found a solution that works for me. Any help would be appreciated.

I have an Array that is populated by text and pictures. I am then showing the information in a tableView. If I were to use static sized cells everything works out fine, but the amount of text changes, so I have also implemented the heightForRowAtIndexPath method. This works as well, until I scroll all the way down to the bottom.

After that, when I scroll back up, all the cell heights change and the display gets all jumbled. Some text gets cut off, pictures get chopped and some of the cells only have the last portion of text. I really think it has something to do with reusing the cells, but I don’t know how to attack this problem. Below is my code for cellForRowAtIndexPath and heightForRowAtIndexPath.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

if (cell == nil)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[NSString class]])
{
    NSString *label = [_theBigArray objectAtIndex:indexPath.row];
    CGSize stringSize = [label sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:NSLineBreakByWordWrapping];

    UITextView *textV = [[UITextView alloc] initWithFrame:CGRectMake(5, 5, 290, stringSize.height +50)];
    textV.font = [UIFont systemFontOfSize:15];
    textV.text = [_theBigArray objectAtIndex:indexPath.row];
    textV.textColor = [UIColor blackColor];
    textV.editable = NO;
    [cell.contentView addSubview:textV];
}
else if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[UIImage class]])
{
    UIImageView *imageV = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 290, 100)];
    imageV.contentMode = UIViewContentModeScaleAspectFit;
    imageV.image = [_theBigArray objectAtIndex:indexPath.row];
    [cell.contentView addSubview:imageV];
}

return cell;
[tableView reloadData];
}

For heightForRowAtIndexPath

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{


int rowHeight = 0.0f;

if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[NSString class]])
{    
    NSString *temp = [_theBigArray objectAtIndex:indexPath.row];
    CGSize size = [temp sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:NSLineBreakByWordWrapping];
    rowHeight = size.height+50;
}
else if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[UIImage class]])
{
    rowHeight = 115.0f;
}

//NSLog(@"rowHeight is %i", rowHeight);

return rowHeight;
[tableView reloadData];
}

I even tried to make two different cells and call them separately, but the same thing happens. I did still use the same heightForRowAtIndexPath method.

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

UITableViewCell *newCell = [[UITableViewCell alloc] init];

if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[NSString class]])
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }

    NSString *label = [_theBigArray objectAtIndex:indexPath.row];
    CGSize stringSize = [label sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:NSLineBreakByWordWrapping];
    UITextView *textV = [[UITextView alloc] initWithFrame:CGRectMake(5, 5, 290, stringSize.height +50)];
    textV.font = [UIFont systemFontOfSize:15];
    textV.text = [_theBigArray objectAtIndex:indexPath.row];
    textV.textColor = [UIColor blackColor];
    textV.editable = NO;
    [cell.contentView addSubview:textV];

    newCell = cell;
}
else if ([[_theBigArray objectAtIndex:indexPath.row] isKindOfClass:[UIImage class]])
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PictureCell"];

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"PictureCell"];
    }

    UIImageView *imageV = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 290, 100)];
    imageV.contentMode = UIViewContentModeScaleAspectFit;
    imageV.image = [_theBigArray objectAtIndex:indexPath.row];
    [cell.contentView addSubview:imageV];

    newCell = cell;

}

return newCell;
[tableView reloadData];
}

Any ideas?

Upvotes: 2

Views: 2733

Answers (1)

Aaron Brager
Aaron Brager

Reputation: 66242

The main problem is that you're adding subviews to cells every time they scroll in, but when a cell is reused, it will already have those subviews added. (That is, when a cell is reused, it will already have a UITextView or UIImageView depending on the reuse identifier.)

You need to check if these subviews exist first; this is commonly done by using the -[UIView viewWithTag] method, or by subclassing UITableViewCell and assigning each view as a property.

(You can take a look at the SO question How to get other control value from UITableViewCell? to see how to use viewWithTag. I would avoid subclassing UITableViewCell until you're more comfortable with the out-of-the-box implementation.)

Also, this line of code:

UITableViewCell *newCell = [[UITableViewCell alloc] init];

is a terrible idea, because you are creating a new UITableViewCell without checking to see if you can reuse one first. This defeats the entire purpose of reusing cells, which is fast scrolling performance. Instead, just declare it without initializing it:

UITableViewCell *newCell;

Also, in heightForRowAtIndexPath, you are

  • declaring rowHeight as an int (it should be a CGFloat)
  • trying to call reloadData after the method returns (which will never happen, but you should never try to call reloadData from this method)

Upvotes: 2

Related Questions