iosfreak
iosfreak

Reputation: 5238

UITableViewCells are out of order

Okay, I am having another UITableView problem. For some reason the indexPath.row is all jumbled up. When I comment out the if statement that sets up the cell, everything works fine. The NSLogs tell me that they are loading in order, but all the cells are out of order.

It also seems as if they repeat; I only see 8 cells, and they repeat over and over.

Here's my code:

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";
NSLog(@"row: %d",indexPath.row);

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.backgroundColor = [UIColor clearColor];
    cell.contentView.backgroundColor = [UIColor clearColor];

    // Add subviews like this:
    // [[cell contentView] addSubview:objectName];
    // And I get the row number like this: indexPath.row when getting objects from the array

}

return cell;
}

Upvotes: 0

Views: 999

Answers (2)

StephenAshley.developer
StephenAshley.developer

Reputation: 1064

The way you have it set up now, cell.selectionStyle, cell.backgroundColor, and cell.contentView.backgrounColor, etc., only get set when if (cell == nil) is true. You need to move that code outside the if statement block, so that it gets called both when dequeueReusableCellWithIdentifier: produces a cell and when it has no cells in inventory and produces nothing (i.e., nil).

Upvotes: 0

NJones
NJones

Reputation: 27147

To use your code:

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

static NSString *CellIdentifier = @"Cell";
NSLog(@"row: %d",indexPath.row);

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        cell.backgroundColor = [UIColor clearColor];
        cell.contentView.backgroundColor = [UIColor clearColor];

        // Add subviews like this:
        // [[cell contentView] addSubview:objectName];


    }
     ### Move this here ###
// And I get the row number like this: indexPath.row when getting objects from the array
return cell;
}

" I only see 8 cells, and they repeat over and over." Correct.

What your missing is that that is how it is supposed to work. That's why only if the cell is nil are you alloc & init'ing a new cell. So you alloc and init and set the colors and add subviews in the if statement. Then after the if(cell==nil) you know you have a valid cell to populate with some data according to the indexPath variable passed in.

The problem is that now you are setting up the cell when it is nil and assigning all of the displayed data according to the indexPath passed in. The problem is cell is not nil the second time it's used so the data is never changed.

To address your speed comment further, I'll use an old fallback example.

- (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];
        UILabel *hugeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height)];
        hugeLabel.tag = 300;
        [cell addSubview:hugeLabel];
    }
    [(UILabel *)[cell viewWithTag:300] setText:[arrayOfStrings objectAtIndex:indexPath.row]];
    return cell;
}

If you look at the sample above, you'll see that we add a UILabel to the cell setting it's tag to 300. Then after the if statement we will have either a brand new cell or a reused cell with text already in the label. No matter either way we simply change the text of the existing label to whatever it should be considering the row. In this way we avoid creating views over and over.

If you are dead-set on caching your UITableViewCells you could do so like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row < _cells.count){
        return [_cells objectAtIndex:indexPath.row]; // _cells is an NSMutableArray setup in viewDidLoad
    }
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@""];
    cell.textLabel.text = [source objectAtIndex:indexPath.row]; // source is an NSArray of NSStrings I set up in viewDidLoad
    [_cells addObject:cell];
    return cell;
}

Note When running this on device don't be surprised when in the console you see Received memory warning What's efficient & what's easy are often not the same.

Upvotes: 1

Related Questions