views:

102

answers:

2

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 before 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?

A: 

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.

SoftmasterG
Could you you please be more specific? Which code is going to expect, mine or framework's? Where should I return nil?
Michael
A: 

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.

xhan
check live demo codes here:http://github.com/erica/iphone-3.0-cookbook-/blob/master/C11-Tables/13-Enabling%20Reordering/main.m
xhan
Reorder the datasource items? I'm not sure if I understand how would I do that. In this controller I just pass an object from `fetchedResultsController` to other view controller, where it's modified and at that time the delegates of `NSFetchedResultsController` are fired. The demo codes don't use `NSFetchedResultsController`, so I can't figure out...
Michael
As a matter of fact, using `NSFetchedResultsController` makes datasource modifications first, then calling `didChangeObject` to update data table. Still confused and looking for solution.
Michael