【问题标题】:NSFetchedResultsController object deleted from UITableView on update更新时从 UITableView 中删除的 NSFetchedResultsController 对象
【发布时间】:2014-03-31 20:43:58
【问题描述】:

请轻点,这是我第一次发帖。

当我更新子类 NSManagedObject 并使用获取的结果控制器执行保存时,它不是在“didChangeObject”中调用 NSFetchedResultsChangeUpdate,而是调用 NSFetchedResultsChangeDelete。保存工作并更新对象,但随后立即将其从 UITableView 中删除。如果我退出并返回,则该对象会随着更新重新出现在 tableview 中,因此我知道它正在将其保存到 DB。

我正在使用 XCODE 5 为一个简单的任务应用程序开发代码。我使用的是 iPhone 4S 而不是模拟器来测试它。它工作正常,直到我对代码的另一部分进行了一些修改(我认为未连接,现在它不起作用。

我有一个排序顺序,当我更新一个对象并且它改变了排序顺序时,UITableViewCells 的移动有一个很好的动画......现在我必须再次强制获取并执行 [self.表视图重新加载数据]。这是一个 hack,因为我没有任何动画,但这是我可以更新它的唯一方法:

我有一个prepare for segue方法:

- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
// configure the destination view controller:
if ( [segue.destinationViewController isKindOfClass: [ShowEditTaskTableViewController class]])
{

    if ([sender isKindOfClass:[UITableViewCell class]] )
    {


        NSIndexPath *indexPath = [self.taskTableView indexPathForCell:sender];
        [self.taskTableView deselectRowAtIndexPath:indexPath animated:YES];

        // Pass the selected task to the new view controller.
        ShowEditTaskTableViewController *detailViewController = segue.destinationViewController;

        Task *info = [self.fetchedResultsController objectAtIndexPath:indexPath];
        detailViewController.editedObject = info;
    }
    else
    {

        NSIndexPath *indexPath = [self.taskTableView indexPathForCell:sender];
        [self.taskTableView deselectRowAtIndexPath:indexPath animated:YES];
        ShowEditTaskTableViewController *detailViewController =           segue.destinationViewController;

        Task *info = (Task *)[NSEntityDescription insertNewObjectForEntityForName:@"Task" inManagedObjectContext:self.managedObjectContext];
        detailViewController.editedObject = info;




    }

}

}

这里没有什么特别的。 “任务”是我的 NSManagedObject。

我有一个倒带(编辑:在评论后将倒带更改为倒带)segue,在它被调用之前,我设置了将设置存储在editedObject中的变量。:

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

if ([segue.identifier isEqualToString:@"addNewTaskSave"]) {
    // Note: This is an unwind segue to go back to the previous screen.

    self.dueDate = self.taskDatePicker.date;
    self.description = self.taskDescriptionTextView.text;
    self.priority = self.taskPrioritySegment.selectedSegmentIndex;
    }
}

在它调用的 RootViewController 中:

- (IBAction)addNewTaskSave:(UIStoryboardSegue *)segue
{

ShowEditTaskTableViewController *controller = segue.sourceViewController;

controller.editedObject.shortDesc = controller.description;
controller.editedObject.priority = @(controller.priority);

controller.editedObject.dueDate = controller.dueDate;
controller.editedObject.completed = @0;

NSError *error;
if (![self.managedObjectContext save:&error]) {
    ...
}

..... }

它使用标准的 didChangeObject 委托方法。在我明确更改某些内容之前,这一直很好。

现在保存后,它会设置 NSFetchedResultsChangeDelete 选项并删除表格行。

解决方法是添加:

self.fetchedResultsController = nil;

if (![[self fetchedResultsController] performFetch:&error]) {
   ...
}
[self.taskTableView reloadData];

但是,这意味着实际观察到的只是 Table View 的直接更新,没有动画。

没有此代码,您可以观察到表格行的动画删除。如果我退出并重新启动,我编辑的对象就在那里。

在过去 4 周(我一直在为 IOS 编写代码的时间)一直是我的伙伴,我搜索了 SO,但找不到任何关于这种行为的信息。

任何帮助将不胜感激。

编辑:

在保存之前,controller.editedObject isDeleted = NO, isUpdated = YES,所以我看不出它为什么要设置 NSFetchedResultsControllerChangeDelete。

【问题讨论】:

  • 向 NSFetchedResultsChangeDelete 案例添加断点并检查调用堆栈。您也许可以看到删除更改的来源。
  • Matthias - 感谢您的评论。不幸的是,我对 Xcode 不是很精通。我已经设置了一个断点,但不知道如何检查调用堆栈。不过会搜索一下,谢谢

标签: ios core-data uitableview nsfetchedresultscontroller


【解决方案1】:

我只讨论你的一段代码;这可能与所描述的获取结果控制器问题有任何关系。

你所谓的 unwind/reverse segue 让我很难过。我不认为prepareForSegue:sender: 与正式的放松赛格相关。因此,也许您并没有真正使用官方的展开转场(它是通过从按钮拖动到情节提要场景下方的“退出”图标来创建的)。

除非您指的是展开转场,否则没有“反向”转场之类的东西可以将您带回上一个屏幕。例如,push segue 没有单独的对应项,称为“pop”segue。

我怀疑您在情节提要的两个场景之间有交错。换句话说,您创建了从场景 A 到场景 B 的转场,并且您创建了从场景 B 到场景 A 的转场。如果真的是这样,我会避免这样做,因为它非常规。也许花一些时间来复习 segues。

好的,我将简要介绍获取结果控制器的问题。如果由获取结果控制器填充的表视图中的数据有用户驱动的更改,那么您将需要设置一个标志并暂时“关闭”获取结果控制器委托方法。在“用户驱动更新”下的NSFetchedResultsControllerDelegate 协议文档中简要提到了这个问题。

【讨论】:

  • bilobatum - 感谢您的回复。你是对的,这是一个放松的转场,我使用了错误的术语。我从一个 SO 答案中复制了这个,所以假设它是正确的。看来答案比较简单,看我在matthias的建议后找到的答案
【解决方案2】:

看来我对 SO 的精练不是很好。看这里https://stackoverflow.com/a/18998335/3482632

我添加了一个 UISegmentControl 来过滤并使用它的段索引的 int 值。

 int index = self.filterSegment.selectedSegmentIndex;
 NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"filter == %d", index]];
 [fetchRequest setPredicate:predicate];

我的 NSManagedObject 子类中的“过滤器”是一个 NSNumber。当我将 NSPredicate 更改为

 NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"filter == %@", @(index)]];

一切正常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多