Question

Edit: The solution for this answer is related to iOS7 sometimes returning NSIndexPath and other times returning NSMutableIndexPath. The issue wasn't really related to begin/endUpdates, but hopefully the solution will help some others.


All - I'm running my app on iOS 7, and I'm running into problems with the beginUpdates and endUpdates methods for a UITableView.

I have a tableview that needs to change the height of a cell when touched. Below is my code:

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

        // If our cell is selected, return double height
        if([self cellIsSelected:indexPath]) {
            return 117;
        }

        // Cell isn't selected so return single height
        return 58;

}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

        ChecklistItemCell *cell = (ChecklistItemCell *)[self.tableview cellForRowAtIndexPath:indexPath];
        [cell.decreaseButton setHidden:NO];
        [cell.increaseButton setHidden:NO];

        // Toggle 'selected' state
        BOOL isSelected = ![self cellIsSelected:indexPath];

        DLog(@"%@", selectedIndexes);

        DLog(@"is selected: %@", isSelected ? @"yes":@"no");
        // Store cell 'selected' state keyed on indexPath
        NSNumber *selectedIndex = @(isSelected);
        selectedIndexes[indexPath] = selectedIndex;

        [tableView beginUpdates];
        [tableView endUpdates];

}

The beginUpdates and endUpdates methods are working pretty inconsistently. The didSelectRowAtIndexPath method gets called correctly on each touch(I thought at first the UI was getting blocked), and the selectedIndexes is storing alternating values correctly. The issue is, sometimes I touch a table cell and all the methods are called properly, but the cell height doesn't change. Anyone know what's going on?

Was it helpful?

Solution

There is a change in behavior in iOS7 where index paths are sometimes instances of NSIndexPath and other times UIMutableIndexPath. The problem is that isEqual between these two classes is always going to return NO. Thus, you cannot reliably use index paths as dictionary keys or in other scenarios that rely on isEqual.

I can think of a couple of viable solutions:

  1. Write a method that always returns an instance of NSIndexPath and use it to generate keys:

    - (NSIndexPath *)keyForIndexPath:(NSIndexPath *)indexPath
    {
        if ([indexPath class] == [NSIndexPath class]) {
            return indexPath;
        }
        return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
    }
    
  2. Identify rows by the data, not the index path. For example, if your data model is an array of NSString, use that string as the key into your selectedIndexes map. If your data model is an array of NSManagedObjects, use the objectID, etc.

I'm successfully using both of these solutions in my code.

EDIT Modified solution (1) based on @rob's suggestion of returning NSIndexPaths rather than NSStrings.

OTHER TIPS

endUpdates shouldn't be called immediately after beginUpdates. The latter's documentation states, "Begin a series of method calls that insert, delete, or select rows and sections of the receiver." That suggests that it should be called in willSelectRowAtIndexPath: and endUpdates should be called in didSelectRowAtIndexPath.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top