Reputation: 9490
I know there's similar questions to this, but the approved answers don't seem to be working for me. So, my scenario is that I have a UITableView
and I want to add and remove items by scanning a bar code. All that works fine except I can't get the UITableView
to display the updated information. The problem specifically comes from the tableView:cellForRowAtIndexPath:
method on every other reload after the initial one. More specifically the cell is always not nil
, so it skips over the new cell creation logic.
For other questions like mine, the answer is that the cell identifier is the problem. Well, I tried messing around with that and it didn't work. Here's my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.section < vehicle.inventoryCategoriesCount) {
cell = [tableView dequeueReusableCellWithIdentifier:@"ModelCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"ModelCell"] autorelease];
NSString *model = [[[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"category == %@", [vehicle.inventoryCategories objectAtIndex:indexPath.section]]] valueForKeyPath:@"@distinctUnionOfObjects.model"] objectAtIndex:indexPath.row];
cell.textLabel.text = model;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"model == %@", model]] count]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"RemoveCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"RemoveCell"] autorelease];
cell.textLabel.text = @"Remove an Item";
cell.textLabel.textColor = [UIColor redColor];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
return cell;
}
So, in this example code, I have two different cell identifiers for the separate sections. They are ModelCell and RemoveCell. Well, they don't work as a solution because nothing happens. If I change the cell identifier when I'm allocating a new cell it works because it's simply wiping everything since the identifiers don't match, but I'm going to assume that that is wrong and that there should be a better solution to this issue or I'm simply not doing something right.
I'd appreciate some help on this. I've spent a day on this chunk of code so far and I have not gotten anywhere and I'd like to get it fixed and move on to other part of my app...
Thanks in advance for any help!
UPDATE
Thanks to @fluchtpunkt the problem has been resolved. For anyone else who may run into this in the future, here's the corrected code. I decided to make the identifier even more unique by appending the section number to it.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.section < vehicle.inventoryCategoriesCount) {
NSString *identifier = [NSString stringWithFormat:@"ModelCell-%d", indexPath.section];
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSString *model = [[[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"category == %@", [vehicle.inventoryCategories objectAtIndex:indexPath.section]]] valueForKeyPath:@"@distinctUnionOfObjects.model"] objectAtIndex:indexPath.row];
cell.textLabel.text = model;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"model == %@", model]] count]];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"RemoveCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"RemoveCell"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textColor = [UIColor redColor];
}
cell.textLabel.text = @"Remove an Item";
}
return cell;
}
UPDATE
The final code version which corrects my misunderstanding of how the identifier works. I figured I'd keep it simple, so I named the identifiers after the cell style type.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.section < vehicle.inventoryCategoriesCount) {
cell = [tableView dequeueReusableCellWithIdentifier:@"Value1"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"Value1"] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSString *model = [[[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"category == %@", [vehicle.inventoryCategories objectAtIndex:indexPath.section]]] valueForKeyPath:@"@distinctUnionOfObjects.model"] objectAtIndex:indexPath.row];
cell.textLabel.text = model;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"model == %@", model]] count]];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"Default"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Default"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textColor = [UIColor redColor];
}
cell.textLabel.text = @"Remove an Item";
}
return cell;
}
Upvotes: 0
Views: 1896
Reputation: 1712
You definitely reuse your cells . if you put your configuration inside if(cell == nil) , it works when no cell is reused . so please put the following code snippet outside of if(cell == nil) condition------------
if(cell == nil) {`` // enter code here } NSString *model = [[[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"category == %@", [vehicle.inventoryCategories objectAtIndex:indexPath.section]]] valueForKeyPath:@"@distinctUnionOfObjects.model"] objectAtIndex:indexPath.row];
cell.textLabel.text = model;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"model == %@", model]] count]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Upvotes: 0
Reputation: 1300
Alex - if I look at your code right at the top, you're doing this:
cell = [tableView dequeueReusableCellWithIdentifier:@"ModelCell"];
However, all the examples (and my code) require a static NSString for the CellIdentifier
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
This then works the intended way.
Upvotes: 0
Reputation: 90117
put your cell configuration outside of if (cell == nil) { ... }
The if condition is only true if no cell could be reused. And you definitely want to reuse your cells. So configure them when you have a valid cell.
Like this:
if (indexPath.section < vehicle.inventoryCategoriesCount) {
cell = [tableView dequeueReusableCellWithIdentifier:@"ModelCell"];
if (cell == nil) {
// only create a new cell if a dequeue was not successful.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"ModelCell"] autorelease];
}
// whatever happened before you have a valid cell here.
// configure cell:
NSString *model = [[[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"category == %@", [vehicle.inventoryCategories objectAtIndex:indexPath.section]]] valueForKeyPath:@"@distinctUnionOfObjects.model"] objectAtIndex:indexPath.row];
cell.textLabel.text = model;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [[vehicle.inventory filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"model == %@", model]] count]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
if you want to optimize your code you could move the options that are the same for every cell inside the (cell == nil)
condition. For example setting the selectionStyle, or changing the textcolor
Upvotes: 3