简体   繁体   中英

UITableView with multiple NSFetchedResultsControllers causing Assertion failure

I have a UITableView with 3 sections and each section is being fed through unique NSFetchedResultsController.

I am getting Assertion failure from the NSFetchedResultsController -controllerDidChangeContent , when inserting, updating...the table.

My guess is issue with indexPaths coming in method below as every controller has only single section (0) and for controller in section 0 the failure doesn't happen.

- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath
{
    switch(type)
    {
        case NSFetchedResultsChangeInsert:
            [[self atableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
            break;
        case NSFetchedResultsChangeDelete:
            [[self atableView] deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:(DashboardViewCell *) [[self atableView] cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [[self atableView] deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [[self atableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    } 
}

So my question is, how can I determine which controller (from which section) is being processed and modify the indexPath accordingly and if that's the right way of doing it? And possibly any examples of using several nsfetchedresultscontrollers with one uitableview.

So my guess was actually correct:

as each controller enters following function

  • (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath

the indexPath is incorrect for every controller apart from the one that is setup for section 0.

Each controller has only single section - 0 in my case but more importantly, these sections do not correspond to the table sections.

So the workaround I currently implemented (not great, so will probably rework) is to check for cacheName of the controller and based on that modify the section for indexPath/newIndexPath

something like this:

if([[controller cacheName] isEqualToString:@"sectionX"])
{
  indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:<add correct section>];
  newIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:<add correct section>];
}

I am not sure it is the FetchedResultController.

The insert and delete row call will at some point ask the tableView to reload, it will then ask the delegate and datasource methods for numberOfRowsInSection, cellForRowAtIndexPath etc.

What probably happens is that the model and the tableView are out of sync and it causes the controller to throw a warning.

    [tableView beginUpdates];

     if (editingStyle == UITableViewCellEditingStyleDelete) {

        NSMutableDictionary *item = [self.itemList objectAtIndex:indexPath.row];
    // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [self.itemList removeObjectAtIndex:indexPath.row];      

}   
    [tableView endUpdates];

Try something like this, where all changes to the tableView and the underlying model are wrapped in the beginUpdates and endUpdates. Those will cause the tableView to wait with the drawing of the cell until you give the 'OK'.

If the above is not the case here is how I usually deal with multiple sections in a tableView.

In your header you declare a typedef enum for the sections;

typedef enum {

    SectionTypeName,
    SectionTypeAge,
    SectionTypeSkills,

} SectionType;

//in the implementation

switch (indexPath.section) {
    case SectionTypeName:
        //do thing in the name section
        break;
    case SectionTypeAge:
        //do thing in the name section
        break;
    case SectionTypeSkills:
        //do thing in the name section
        break;          
    default:
        break;
}

I have the switch() in almost every tableView delegate/datasource method. This makes it very easy to figure out which section is being processed and what it does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM