首页 文章

NSFetchedResultsController 's delegate doesn' t在谓词更改后触发

提问于
浏览
7

我已经对这个问题进行了几个小时的故障排除,到目前为止没有成功 . 我已经在这里阅读了所有可能与我的问题有关的问题 .

我有两个实体(A,B)通过像这样的一对多关系以树状结构相互连接:A <--- >> B.

有一个由NSFetchedResultsController支持的UITableViewController,用于显示与实体A的选定对象相关的实体B的所有对象 . 我正在使用谓词:

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

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"B" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

[fetchRequest setFetchBatchSize:20];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"controlDate" ascending:NO];
NSArray *sortDescriptors = @[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];

NSMutableArray *subpredicates = [NSMutableArray array];
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"isRelatedTo = %@", selectedObjectOfA];
[subpredicates addObject:predicate1];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"syncStatus != %d", ObjectDeleted];
[subpredicates addObject:predicate2];

NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
[fetchRequest setPredicate:predicate];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;

NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

return _fetchedResultsController;
}

一切都适用于添加,删除和编辑对象 . 表格相应更新 . 我的类符合NSFetchedResultsControllerDelegate,委托方法是样板文件 .

但我希望用户能够通过点击“向上”/“向下”按钮循环浏览实体A的所有对象,而无需回到导航堆栈中(就像可以直接在Mail应用程序中循环浏览电子邮件一样) . B的相关对象的数量取决于所选择的A对象(在无和之间,并且假设为20) .

问题就出现了:我的目的是通过更改谓词(不是它的结构或语法,而只是'selectedObjectOfA'的值和调用来刷新FRC的结果)

[self.fetchedResultsController performFetch:&error];

其余的全部应由FRC完成 .

第一个问题是:这种方法是否正确?

以下是我已尝试过的方案:

  • 只是将'selectedObjectOfA'设置为新值并且调用performFetch总是显示相同的结果(因为fetchRequest和谓词没有被更新我想)

  • '重置'这样的FRC的fetchrequest

NSMutableArray *subprediactes = [NSMutableArray array];
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"isRelatedTo = %@",    self.selectedObjectOfA];
[subprediactes addObject:predicate1];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"syncStatus != %d", ObjectDeleted];
[subprediactes addObject:predicate2];

NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subprediactes];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];

更新FRC并返回正确的数据,但我认为应用程序崩溃的行数不正确

'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 (5), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

因为NSFetchedResultsController委托方法不会被调用 . 因此,tableView中的行数不适应新结果(根据错误消息,返回的对象的数量是正确的) .

第二个问题是:为什么委托在添加/删除对象时触发,而不是在谓词(因此结果)发生变化之后触发?

先感谢您 .

2 回答

  • 4

    你必须打电话

    [tableView reloadData];
    

    修改获取请求后 . 获取的结果控制器仅跟踪 performFetch 之后发生的更改 .

    您可以尝试以下方法来获取表视图的动画更新(但我还没有尝试过,所以我不确定它是否有效):

    • 拨打 [tableView beginUpdates] .

    • 使用 [tableView deleteRowsAtIndexPaths:...] 删除所有"old"行 .

    • 更新获取的结果控制器获取请求 .

    • 致电 [fetchedResultsController performFetch:...] .

    • [fetchedResultsController fetchedObjects] 的所有对象调用 [tableView insertRowsAtIndexPaths:...] 以插入所有"new"行 .

    • 致电 [tableView endUpdates] .

  • 4

    在谓词更改后设置tableview的动画:

    NSFetchRequest* fetchRequest = self.fetchedResultsController.fetchRequest;
    fetchRequest.predicate = newPredicate;
    
    NSArray* objectsBefore = self.fetchedResultsController.fetchedObjects;
    
    [self.fetchedResultsController performFetch:nil];
    
    NSArray* objectsAfter = self.fetchedResultsController.fetchedObjects;
    
    
    [self.tableView beginUpdates];
    
    if (objectsBefore.count > 0)
    {
        for (id objectBefore in objectsBefore)
        {
            if ([objectsAfter indexOfObject:objectBefore] == NSNotFound)
            {
                NSIndexPath* indexPath = [NSIndexPath indexPathForRow:[objectsBefore indexOfObject:objectBefore] inSection:0] ;
    
                [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
            }
        }
    }
    
    if (objectsAfter.count > 0)
    {
        for (id objectAfter in objectsAfter)
        {
            if ([objectsBefore indexOfObject:objectAfter] == NSNotFound)
            {
                NSIndexPath* indexPath = [NSIndexPath indexPathForRow:[objectsAfter indexOfObject:objectAfter] inSection:0];
    
                [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
            }
        }
    }
    
    [self.tableView endUpdates];
    

相关问题