开发者

Updating UITableView will cause exception

开发者 https://www.devze.com 2023-01-11 14:57 出处:网络
I have UITableViewController subclassed and implemented the following NSFetchedResultsController with its delegates

I have UITableViewController subclassed and implemented the following

  1. NSFetchedResultsController with its delegates
  2. tableView:sectionForSectionIndexTitle:atIndex:
  3. tableView:titleForHeaderInSection:

In controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:

case NSFetchedResultsChangeMove:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
           withRowAnimation:UITableViewRowAnimationFade];
    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
           withRowAnimation:UITableViewRowAnimationFade];
    break;

In data model for the given entity I have transient property uppercaseFirstLetterOfName which will return first letter of persistent property.

This is all to achieve Alphabetical sections for table items and side index.

Now if I have a single record for a section, then I rename it so it will change the section, I get the following exception, which happens somewhere after NSFetchedResultsChangeMove.

*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-984.38/UITableView.m:774

Exception was caught during Core Data change processing: Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section bef开发者_开发百科ore the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).

Any ideas?

UPD some more code:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
    NSInteger count = [[fetchedResultsController sections] count];
    return count;
}

- (NSInteger)tableView:(UITableView *)tableView
                         numberOfRowsInSection:(NSInteger)section 
{
    NSInteger numberOfRows = 0;
    if ([[fetchedResultsController sections] count] > 0) 
    {
        id <NSFetchedResultsSectionInfo> sectionInfo =
              [[fetchedResultsController sections] objectAtIndex:section];
        numberOfRows = [sectionInfo numberOfObjects];
    }
    return numberOfRows;
}

...

    NSSortDescriptor* sortByWordDescriptor = [[NSSortDescriptor alloc] 
                        initWithKey:@"subject" ascending:YES];
    NSArray* sortArray = [[NSArray alloc]
                        initWithObjects:sortByWordDescriptor, nil];
    [fetchRequest setSortDescriptors:sortArray];

    NSFetchedResultsController* controller = [[NSFetchedResultsController alloc]
                        initWithFetchRequest:fetchRequest 
                        managedObjectContext:managedObjectContext 
                        sectionNameKeyPath:@"uppercaseFirstLetterOfName" 
                        cacheName:@"Root"];

UPD(patched): At the moment I patched the code like this:

    case NSFetchedResultsChangeMove:
        NSUInteger tableSectionCount = [self.tableView numberOfSections];
        NSUInteger modelSectionCount = [[controller sections] count];
        if (modelSectionCount > tableSectionCount) 
        {
            [self.tableView insertSections:[NSIndexSet
                                indexSetWithIndex:[newIndexPath section]]
                                withRowAnimation:UITableViewRowAnimationNone];
        }
        else if(modelSectionCount < tableSectionCount)
        {
            if (tableSectionCount > 1) 
            {
                [self.tableView deleteSections:[NSIndexSet
                                 indexSetWithIndex:[indexPath section]] 
                                 withRowAnimation:UITableViewRowAnimationNone];
            }
        }
        [tableView deleteRowsAtIndexPaths:[NSArray
                                 arrayWithObject:indexPath]
                                 withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray
                                 arrayWithObject:newIndexPath] 
                                 withRowAnimation:UITableViewRowAnimationFade];

        break;

So far no crash, but is this correct solution?


I think I know what's happening to you. Make sure you have:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

The documentation says about "beginUpdates" method:

Call this method if you want subsequent insertions, deletion, and selection operations (for example, cellForRowAtIndexPath: and indexPathsForVisibleRows) to be animated simultaneously.

which is what you're doing with:

[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];

Cheers


The code that handles the sections is going to expect an item to be at a certain index but that item is no longer there and it doesn't seem you have coded for that scenario by returning nil when there's no item there.


you should modify the datasource array before do any insert or delete method of tableView!

    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
       withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
       withRowAnimation:UITableViewRowAnimationFade];

and NSFetchedResultsChangeMove should finish your UI movement action. so what you need do is to reorder the datasorce items.

0

精彩评论

暂无评论...
验证码 换一张
取 消