Search code examples
iosobjective-cuitableviewcocoa-touchios5

Why does reloadRowsAtIndexPaths not work for iOS 5.0?


SOLVED: See my answer (and possible explanation) below.

I'm making an app that works on iOS 5.1 devices, but not on iOS 5.0 devices. Here is the trouble code that works on 5.1 but NOT on 5.0:

- (void) expandIndexPath: (NSIndexPath *) indexPath afterDelay: (BOOL) delay
{
    NSIndexPath *oldSelectedIndexPath = [NSIndexPath indexPathForRow:self.mySelectedIndex inSection:0];
    self.mySelectedIndex= indexPath.row; 
//  [self.myTableView beginUpdates];
    [self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:self.mySelectedIndex inSection:0], oldSelectedIndexPath,nil] withRowAnimation:UITableViewRowAnimationNone]; 
//  [self.myTableView endUpdates];
}

Even more curiously, it works in iOS 5.0 if I replace the reloadRowsAtIndexPaths line with [self.myTableView reloadData];. Why is this? Did 5.0 have a bug regarding the reloadRowsAtIndexPaths line? I've tried it on 5.0 with and without begin/endUpdates lines and neither works.

EDIT: To be more specific, when I run the app on iOS 5 it crashes with the following error:

* Assertion failure in -[_UITableViewUpdateSupport _computeRowUpdates], /SourceCache/UIKit/UIKit-1912.3/UITableViewSupport.m:386 [Switching to process 7171 thread 0x1c03]

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid table view update. The application has requested an update to the table view that is inconsistent with the state provided by the data source.'

EDIT: Here are my UITableViewDataSource methods.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    //I am positive that this will ALWAYS return the number (it never changes)
    return self.myCellControllers.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //I cache my table view cells, thus each time this method gets called it will 
    // return the exact same cell. Yes, I know that most of the time I should be dequeing
    // and reusing cells; just trust me that this time, it's best for me to cache them
    // (There are very few cells so it doesn't really matter)
    CellController *controller = [self.myCellControllers objectAtIndex:indexPath.row];
    return controller.myCell;
}

- numberOfSectionsInTableView: always returns 1;

The only interesting method is

- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row == self.mySelectedIndex)
    {
        return DEFAULT_CELL_HEIGHT + self.expandSize;
    }
    else
    {
        return DEFAULT_CELL_HEIGHT;
    }
}

Just as a quick overview of how this part of the program works, when a user taps a cell, the cell scrolls to the top of the screen. After it is at the top of the screen it's index gets set as self.mySelectedIndex and it expands (gets taller).


Solution

  • I found a solution that works. If I change the expandIndexPath method to this it works:

    - (void) expandIndexPath: (NSIndexPath *) indexPath afterDelay: (BOOL) delay {
        [self.myTableView beginUpdates];
        NSIndexPath *oldSelectedIndexPath = [NSIndexPath indexPathForRow:self.mySelectedIndex inSection:0];
        self.mySelectedIndex= indexPath.row;
        if(oldSelectedIndexPath.row >= 0)
        {
            [self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:self.mySelectedIndex inSection:0],oldSelectedIndexPath, nil] withRowAnimation:UITableViewRowAnimationNone]; 
        }
        else 
        {
            [self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:self.mySelectedIndex inSection:0],nil] withRowAnimation:UITableViewRowAnimationNone]; 
        }
        [self.myTableView endUpdates]; 
    }
    

    As far as I can tell, the problem was that oldSelectedIndexPath was sometimes being set with a negative row value. Thus when I tried to reload an indexPath with a negative row, it crashed in iOS 5.0. It appears that iOS 5.1 fixes this and does more error checking.