开发者

NSFetchedResultsControllerDelegate & UITableViewDelegate Behavior

I have encountered behavior with the FRC and TableView delegates that appears to be inconsistent:

  • The initial call to performFetch causes the delegate methods to be called as expected. However, if I update the predicate on the FRC's baseFetch and then cal开发者_JS百科l performFetch again, the FRC delegate methods are never called, and the TableView is not updated with new data. To force the table to update and display the new data I am explicitly calling reloadData.
  • The initial call to performFetch correctly builds the TableView's sections. If I change FRCs (the new FCR has a different fetchRequest and sectionNameKeyPath) and call performFetch again, the table and sections update to match the new results, as expected. HOWEVER, if I then go back to the original FRC and call performFetch, the sections have the correct names but with the previous FRC's section/row structure. To force the sections to update I am explicitly calling reloadSectionIndexTitles.

My feeling is that the delegates should be firing anytime we call performFetch if something changes. Explicitly calling reloadData and reloadSectionIndexTitles seems like an expensive and unnecessary step since that's what the delegates exist for. Am I missing something?

Here's the relevant code:

NSFetchedResultsControllerDelegate

    - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.myTable beginUpdates];
}


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

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [self.myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[self.myTable cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [self.myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            // Reloading the section inserts a new row and ensures that titles are updated appropriately.
            [self.myTable reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }

}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [self.myTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.myTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.myTable endUpdates];
}

UITableViewDelegate

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return [[currentFCR sections] count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[currentFCR sections] objectAtIndex:section];
    return section.name;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    id <NSFetchedResultsSectionInfo> sectionInfo = [[currentFCR sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }

    // Configure the cell...
    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

Fetching

- (NSFetchedResultsController *)FRC1 {

    if (FRC1 != nil) {
        return FRC1;
    }

    NSFetchRequest *request = [[DataProvider instance] getFetch];
    [[DataProvider instance] sortListByFirstSort:request];
    [request setFetchBatchSize:20];

    FRC1 = [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                                                                   managedObjectContext:[[DataProvider instance] managedObjectContext] 
                                                                     sectionNameKeyPath:@"groupName" 
                                                                              cacheName:nil];
    return FRC1;    

}

- (NSFetchedResultsController *)FRC2 {

    if (FRC2 != nil) {
        return FRC2;
    }

    NSFetchRequest *request = [[DataProvider instance] getFetch];
    [[DataProvider instance] sortListBySecondSort:request]; 


 [request setFetchBatchSize:20];

        FRC2 = [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                                                       managedObjectContext:[[DataProvider instance] managedObjectContext] 
                                                         sectionNameKeyPath:@"name" 
                                                                  cacheName:nil];
        return FRC2;    

    }

    -(void)doFetch{
    NSError *error;
    if (![currentFCR performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }
    [self.myTable reloadData];
    [self.myTable reloadSectionIndexTitles];
}


NSFetchedResultsControllerDelegate methods controllerWillChangeContent:, etc, are only called if a delegate is set on an NSFetchedResultsController instance. I don't see any delegates set in your code.

The initial table load (or explicitly calling reloadTable) will cause the UITableViewDataSource methods to be called, fetching data from the currentFCR. The NSFetchedResultsControllerDelegate methods will not be called if a delegate is not set on an NSFetchedResultsController.

Also, NSFetchedResultsControllerDelegate methods are only called if any of the relevant managed objects change (or are added/deleted) within the NSManagedObjectContext.

If you want to replace the NSFetchedResultsController object you will need to reload the table explicitly.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜