7

我一直在解决这个问题几个小时,至今没有成功。我已阅读所有关于SO的问题,可能与我的问题有关。修改谓词后,NSFetchedResultsController的代理不会触发

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

有一个NSFetchedResultsController的支持表明,在我使用的谓词为相关实体A的选择对象实体B的所有对象的UITableViewController:

- (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的所有对象,而无需返回到导航堆栈中(例如,可以直接在浏览器中循环浏览电子邮件邮件应用程序)。 B的相关对象的数量根据A的选定对象而变化(在无和之间可以说20)。

在这里,问题来了: 我的意图是通过改变谓词刷新FRC的结果(不是它的结构和语法,但“selectedObjectOfA”的唯一价值,并呼吁

[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中的行数不适应新的结果(根据错误消息,返回的对象数是正确的)。

第二个问题是:为什么委托在添加/删除对象时触发,但是在谓词(以及结果)发生改变后触发?

预先感谢您。

回答

3

你必须调用

[tableView reloadData]; 

修改读取请求后。提取的结果控制器仅跟踪performFetch后发生的更改。

你可以尝试下获得表视图的动画更新(但我没有试过呢,所以我不知道,如果它的工作原理):

  • 呼叫[tableView beginUpdates]
  • 使用[tableView deleteRowsAtIndexPaths:...]删除所有“旧”行。
  • 更新获取的结果控制器获取请求。
  • 致电[fetchedResultsController performFetch:...]
  • 请致电[tableView insertRowsAtIndexPaths:...][fetchedResultsController fetchedObjects]的所有对象插入所有“新”行。
  • 致电[tableView endUpdates]
+0

你说得对。我也试过这个,同时它也可以工作!不幸的是,在这种情况下,使用'[tableview beginUpdates]'和'[tableView endUpdates]'的行动画(添加/删除行)会丢失。有没有可能保留它? 如果我让你正确地修改谓词并执行新的读取操作,将重新启动FRC对变化的跟踪,这意味着没有可能的“平滑”转换(因为委托没有被调用)。你可否确认? – Francesco

+0

@FranzBernt:没有“内置”方法来更改FRC获取请求以及动画表视图更新。 - 我有一个想法如何实现这一点(见更新的答案),但我不知道这是否有效。 –

+0

@FranzBernt:请让我知道如果更新方法的作品。 (如果没有,你将不得不使用'reloadData')。 –

4

要断言变更后动画的实现代码如下:

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]; 
+1

真的很适合我,谢谢 – bitwit

相关问题