UISearchController NSFetchResultsController

UISearchController NSFetchResultsController

我收到此错误:

CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to delete row 5 from section 0 which only contains 1 rows before the update with userInfo (null) If I edit first or second not last everything is working.

我有一个 tableview 和 UISearchController,当我从 tableview 触摸行时,它会将我发送到另一个 vc,我可以在其中编辑我的日记。 一切正常,除了当我搜索某些东西时,如果结果是我的 TableView 的最后一个条目,当我尝试编辑和保存时,它会给我上面写的错误。

@implementation PlumbListTableViewController


-(void)viewDidLoad {
    [super viewDidLoad];
  [self.fetchedResultsController performFetch:nil];
 }

-(void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:YES];

   self.searchController =[[UISearchController alloc] initWithSearchResultsController:nil];

    self.searchController.searchResultsUpdater = self;
    self.searchController.dimsBackgroundDuringPresentation = NO;
    self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);

    self.tableView.tableHeaderView = self.searchController.searchBar;
   self.searchController.searchBar.delegate = self;

     self.tabBarController.tabBar.hidden = NO;
    [self showTotalSum];
    [self.tableView reloadData];

}

-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{
[self.tableView reloadData];
}

- (void)searchForText:(NSString *)searchText
{
    CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"AddEntry"];
    fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]];
    if (self.searchController.searchBar.text.length == 0) {
         NSError *error = nil;
        self.filteredList = [coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    } else {

    if (coreDataStack.managedObjectContext) {

   NSPredicate *predicate = [NSPredicate predicateWithFormat:@"titluPlumb contains[c] %@", searchText];
        [fetchRequest setPredicate:predicate];
          NSError *error = nil;
        self.filteredList = [coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    }
    }
}



- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
    NSString *searchString = searchController.searchBar.text;

    [self searchForText:searchString ];
    [self.tableView reloadData];

}

-(void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope
{
    [self updateSearchResultsForSearchController:self.searchController];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {


    if ([segue.identifier isEqualToString:@"edit"]) {
        UITableViewCell *cell = sender;
        NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
        UINavigationController *navigationController = segue.destinationViewController;
        PlumbAddViewController *entryViewController = (PlumbAddViewController *)navigationController.topViewController;

        if (self.searchController.active) {
            entryViewController.entry = [self.filteredList objectAtIndex:indexPath.row];
        } else {
        entryViewController.entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
        }
   }
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.

    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.

    if (self.searchController.active)
    {
           return [self.filteredList count];
      }
    else
    {
        id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
        return [sectionInfo numberOfObjects];
   }

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";
    ConfigureCellPlumb *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    AddEntry *entry = nil;

    if (self.searchController.active){

        entry = [self.filteredList objectAtIndex:indexPath.row];

    } else {

           entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
    }

    cell.titluView.text = entry.titluPlumb;
    cell.descriereView.text = entry.bodyPlumb;

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"MM/dd/yyyy"];
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:entry.date];
    cell.dataLabel.text = [dateFormatter stringFromDate:date];

    if (entry.imageDataPlumb) {
        cell.imagineView.image = [UIImage imageWithData:entry.imageDataPlumb];
    } else {
        cell.imagineView.image = [UIImage imageNamed:@"icn_noimage"];
    }

    return cell;

}


- (NSFetchRequest *)entryListFetchRequest {
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"AddEntry"];

    fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]];

    return fetchRequest;
}

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
    NSFetchRequest *fetchRequest = [self entryListFetchRequest];

    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:coreDataStack.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    AddEntry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];

    CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
    [[coreDataStack managedObjectContext] deleteObject:entry];
    [coreDataStack saveContext];
}



- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {

    [self.tableView beginUpdates];
}

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

    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeUpdate:
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeMove:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
    }
}

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

    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        case NSFetchedResultsChangeUpdate:
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        }

}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {

    [self.tableView endUpdates];

}

@end

更新:

- (NSFetchRequest *)entryListFetchRequest {
    self.searchFetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"AddEntry"];

    self.searchFetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]];





    return self.searchFetchRequest;
}


- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    self.coreDataStack = [CoreDataStack defaultStack];
    NSFetchRequest *fetchRequest = [self entryListFetchRequest];
    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.coreDataStack.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
    _fetchedResultsController.delegate = self;

    if (self.searchController.searchBar.text.length != 0) {
        NSString *searchText = self.searchController.searchBar.text;
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"titluPlumb contains[c] %@", searchText];
        [self.searchFetchRequest setPredicate:predicate];

             NSError *error = nil;
        self.filteredList = [self.coreDataStack.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
    }


return _fetchedResultsController;
}

更新 2

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    self.coreDataStack = [CoreDataStack defaultStack];
    NSFetchRequest *fetchRequest = [self entryListFetchRequest];

    if (self.searchController.searchBar.text.length !=0) {
       NSString *searchText = self.searchController.searchBar.text;
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"titluPlumb contains[c] %@", searchText];
        fetchRequest.predicate = predicate;
    }

    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.coreDataStack.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;
}


- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{

self.fetchedResultsController = 无; [self.tableView 重新加载数据];

    }
}

更新 3

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.

    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.

        id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
        return [sectionInfo numberOfObjects];


}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";
    ConfigureCellPlumb *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    AddEntry *entry = nil;

           entry = [self.fetchedResultsController objectAtIndexPath:indexPath];


    cell.titluView.text = entry.titluPlumb;
    cell.descriereView.text = entry.bodyPlumb;

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"MM/dd/yyyy"];
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:entry.date];
    cell.dataLabel.text = [dateFormatter stringFromDate:date];

    if (entry.imageDataPlumb) {
        cell.imagineView.image = [UIImage imageWithData:entry.imageDataPlumb];
    } else {
        cell.imagineView.image = [UIImage imageNamed:@"icn_noimage"];
    }

    return cell;

}

更新 4

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    self.coreDataStack = [CoreDataStack defaultStack];
   self.searchFetchRequest = [self entryListFetchRequest];

    if (self.searchController.searchBar.text.length !=0) {
       NSString *searchText = self.searchController.searchBar.text;
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"titluPlumb contains[c] %@", searchText];
        self.searchFetchRequest.predicate = predicate;
        [self.tableView reloadData];

    }

    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:self.searchFetchRequest managedObjectContext:self.coreDataStack.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
    _fetchedResultsController.delegate = self;


    return [_fetchedResultsController fetchedObjects];
}


- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
 self.fetchedResultsController = nil;
    [self.tableView reloadData];


     NSLog(@"%@", self.searchFetchRequest.predicate);
}

问题是您的 table 视图同时由 2 个源驱动(在这种情况下),因为您在搜索时没有完全关闭 FRC。因此,您进行搜索并进行编辑,FRC 会看到这一点并尝试更新 table,但是 - table 当前未显示 FRC 行 - 因此有时它会失败。

要么使用 FRC 通过更改获取请求的谓词来进行搜索,要么在搜索时销毁 FRC,并在完成后重新创建它。


因此,将您的 FRC 方法更改为:

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    self.coreDataStack = [CoreDataStack defaultStack];
    NSFetchRequest *fetchRequest = [self entryListFetchRequest];

    if (self.searchController.searchBar.text.length != 0) {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"titluPlumb contains[c] %@", searchText];
        fetchRequest.predicate = predicate;
    }

    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.coreDataStack.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;
}

每当您开始搜索/更改搜索文本/结束搜索时,请执行以下操作:

self.fetchedResultsController = nil;
[self.tableView reloadData];