Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableViewCell Reuse: cell displaying incorrectly - not being reused as expected

I've created a multi-selection list that allows the selection of multiple Ingredients.

In my tableview, cells can either be enabled or disabled based on the list property of an Ingredient. If the Ingredient's list property is set, the cell will be disabled.

However, when a cell is re-used, it's not displaying as I would expect. The images below explain the problem more effectively than I can.

(The ingredients that should not be enabled are: Cake Icing, Condensed Milk and Cournflour.)

  1. The first image shows the three Ingredients disabled and annotated as expected.

  2. However, in the second image, scrolling down reveals that some Ingredients are shown to be disabled (but you can select them, and they have full interaction).

  3. The third image shows the list after scrolling back up to the top. Some Ingredients have been greyed out, and notice how Cornflour is displayed as enabled, even though you can't interact/select it.

The issue is to do with cell reuse. It appears that the cell is not getting "reset" when reused, and so is keeping some of its "old" appearance.

Below is the code from cellForRowAtIndexPath:, as I'm sure this is where the problem is (although I can't see what's wrong).

- (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];
    }    

    // Fetches the corresponding Ingredient from the ingredientArray
    Ingredient *ingredient = [self.ingredientArray objectAtIndex:indexPath.row];

    if ([ingredient.list isEqualToString:@"Lardr"])
    {
        cell.userInteractionEnabled = NO;
        cell.detailTextLabel.text = @"   Already in your Lardr";
    }
    else if ([ingredient.list isEqualToString:@"Shopping List"])
    {
        cell.userInteractionEnabled = NO;
        cell.detailTextLabel.text = @"   Already on your Shopping List";
    }
    else
    {
        cell.userInteractionEnabled = YES;
        cell.detailTextLabel.text = @"";
    }

    // Add a checkmark accessory to the cell if the ingredient is on the selectedIngredients array
    if ([self.selectedIngredients containsObject:ingredient])
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    else
        cell.accessoryType = UITableViewCellAccessoryNone;

    cell.textLabel.text = ingredient.name;

    return cell;
}

I'm at my wit's end trying to figure this out, and I've read all SO questions that are even remotely related, but to no avail. What's the problem?!


My logic is that for each cell, the textLabel, detailTextLabel, userInteractionEnabled and accessoryType properties are set, no matter which execution path through the if statements, so I can't see why, even after reuse, the cell isn't displaying correctly.


EDIT: In an attempt to figure out the root of the problem, I've tried "resetting" the cell back to it's default by adding the following right above the line which fetches the corresponding Ingredient: but to no avail.

cell.userInteractionEnabled = YES;
cell.textLabel.text = nil;
cell.detailTextLabel.text = nil;
cell.accessoryType = UITableViewAccessoryNone;

However, what is interesting and completely illogical -- when I moved the following line cell.textLabel.text = ingredient.name to directly underneath the line that reads Ingredient *ingredient = [self.ingredientArray objectAtIndex:indexPath.row];, absolutely NO styling is applied to any cell, even though userInteraction is set further below (and the relevant cells are disabled as expected).

I'm thinking, does the order in which a cells properties are set matter? I know it shouldn't, but the appearance changes based on the above.


Update: I've solved the issue; see my answer below.

like image 415
TeaPow Avatar asked Apr 03 '13 02:04

TeaPow


2 Answers

Set your cell's color explicitly in willDisplayCell

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{

 Ingredient *ingredient = [self.ingredientArray objectAtIndex:indexPath.row];

    if ([ingredient.list isEqualToString:@"Lardr"])
    {
        cell.textLabel.textColor = [UIColor lightGrayColor];
    }
    else if ([ingredient.list isEqualToString:@"Shopping List"])
    {
        cell.textLabel.textColor = [UIColor lightGrayColor];
    }
    else
    {
        cell.textLabel.textColor = [UIColor blackColor];
    }

}
like image 107
Mihir Mehta Avatar answered Nov 02 '22 22:11

Mihir Mehta


I've solved the issue. The problem was that I was setting userInteractionEnabled BEFORE setting the cell's text (thanks to a link from Carl Veazey in the comments).

I fixed the issue by setting the cell's text before adjusting the userInteractionEnabled property. However, there was no styling applied to the cell (even though the cell was disabled). I therefore had to manually style the cell by setting the cell's textColor. Below is the adjusted code with comments.

- (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];
    } 

    // Fetches the relevent ingredient from the ingredientArray
    Ingredient *ingredient = [self.ingredientArray objectAtIndex:indexPath.row];
    cell.textLabel.text = ingredient.name; // Moved this line before setting userInteractionEnabled

    if ([ingredient.list isEqualToString:@"Lardr"])
    {
        cell.userInteractionEnabled = NO;
        cell.detailTextLabel.text = @"   Already in your Lardr";
        cell.textLabel.textColor = [UIColor grayColor]; // Update the color accordingly
    }
    else if ([ingredient.list isEqualToString:@"Shopping List"])
    {
        cell.userInteractionEnabled = NO;
        cell.detailTextLabel.text = @"   Already on your Shopping List";
        cell.textLabel.textColor = [UIColor grayColor];
    }
    else
    {
        cell.userInteractionEnabled = YES;
        cell.detailTextLabel.text = @"";
        cell.textLabel.textColor = [UIColor blackColor];
    }

    // Add a checkmark accessory to the cell if the ingredient is on the selectedIngredients array
    if ([self.selectedIngredients containsObject:ingredient])
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    else
        cell.accessoryType = UITableViewCellAccessoryNone;

    return cell;
}

Obviously, the order in which you set the properties of a cell should be irrelevant, and so I'm sure that this is a bug. Hope this helps for anybody else who runs into the same issue.

like image 24
TeaPow Avatar answered Nov 03 '22 00:11

TeaPow