【问题标题】:Deleting a cell in search bar table view causes delete to be executed twice删除搜索栏表格视图中的单元格会导致删除执行两次
【发布时间】:2014-01-14 10:16:41
【问题描述】:

我有一个由NSFetchedResultsController 实例的核心数据备份的表视图。 此表有一个显示过滤数据的搜索栏。搜索栏有一个单独的 NSFetchedResultsController(从现在开始为 FRC)实例。

到目前为止一切顺利,数据已按预期获取并显示在表格视图中,并且在搜索栏的表格视图中也正确显示(搜索数据时)。

我的问题是,如果我尝试在搜索栏的表格视图中删除一个单元格,则会收到 coredata 异常:

错误:严重的应用程序错误。从 调用期间 NSFetchedResultsController 的委托 -controllerDidChangeContent:。尝试从第 0 部分中删除第 0 行,该部分在使用 userInfo (null) 进行更新之前仅包含 0 行

进一步检查表明,FRC 的controllerWillChangeContent 方法在一个单元格删除时被调用了两次!。这会导致 deleteRowsAtIndexPaths 为同一个单元格调用两次(因此是 coredata 异常)。

再玩一会我发现这个问题是在搜索后第一次显示搜索栏的表格视图时发生的。 (即使我返回并删除常规表格视图中的单元格也会出现问题)

我已确保 deleteRowsAtIndexPaths 方法在代码中仅被调用一次(在 FRC 的 didChangeObject 委托方法中)。

现在我不确定要显示哪个代码,所以我不会全部泄露。如果您知道问题所在,请告诉我您需要查看哪一个。

这是指示表删除行的代码:

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

    DLog(@"didChangeObject type %lu with object %@", (unsigned long)type, anObject);
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

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

        case NSFetchedResultsChangeMove:
            [self.tableView deleteRowsAtIndexPaths:[NSArray
                                               arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths:[NSArray
                                               arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

删除所选单元格的对象后保存上下文时调用此方法。

我保存上下文的方法:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_managedObjectContext deleteObject:[_notesFetcher objectAtIndexPath:indexPath]];
        NSError *error;
        if (![_managedObjectContext save:&error]) {
            DLog(@"Failed deleting object : %@", [error localizedDescription]);
        }
    }   
}

由于某种原因,此方法被调用了一次,但对同一单元格使用相同的更改类型(2-Delete)调用了两次didChangeObject

更新

当我在方法 didChangeObject 中记录堆栈跟踪时,我得到了这个:

[UITableView animateDeletionOfRowWithCell:] + 107 12 UIKit
0x01316a35-[UITableViewCell_swipeDeleteButtonPushed] + 70 13 libobjc.A.dylib 0x02346874 -[NSObject performSelector:withObject:withObject:] + 77 14 UIKit
0x010a8c8c -[UIApplication sendAction:to:from:forEvent:] + 108 15 UIKit 0x010a8c18 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61 16 UIKit
0x011a06d9 -[UIControl sendAction:to:forEvent:] + 66 17 UIKit
0x011a0a9c-[UIControl_sendActionsForEvents:withEvent:] + 577 18 UIKit 0x0119fd4b -[UIControl touchesEnded:withEvent:] + 641 19 UIKit
0x0141ad7f _UIGestureRecognizerUpdate + 7166 20 UIKit
0x010e5d4a -[UIWindow _sendGesturesForEvent:] + 1291 21 UIKit
0x010e6c6a -[UIWindow sendEvent:] + 1030 22 UIKit
0x010baa36 -[UIApplication sendEvent:] + 242 23 UIKit
0x010a4d9f _UIApplicationHandleEventQueue + 11421

如您所见,表的swipeDeleteButtonPushed 方法在一次删除操作中被调用两次,如上所述。当我只按一次删除按钮时,如何触发两次?有什么想法吗?

【问题讨论】:

  • 能否给出删除表格视图行的代码?
  • 我的猜测是您使用 2 个 FRC 和相同的控制器进行搜索和常规视图。在您的“更新”方法中,您将同一张表更新两次。
  • @MartinR 我添加了相关的删除发生的地方
  • @DanShelly 实际上,我使用相同的控制器作为两个 FRC 的代表,但我确实在需要时使用了正确的控制器(搜索和选择单元格调用正确的 segue 并显示正确的数据)我在某处读到self.tableView 始终指向当前表视图(即,如果显示搜索栏的表视图,则 self.tableView 将指向该表)并且应该在表的委托方法中使用。
  • @giorashc:这是不正确的。您必须更新self.tableView(始终是“主”表视图)或self.searchDisplayController.searchResultsTableView,具体取决于搜索是否处于活动状态。所以@Dan 似乎走在了正确的轨道上。

标签: core-data ios7 tableview searchbar


【解决方案1】:

您需要始终检查哪个控制器正在调用您的委托方法并采取相应措施。同样,您需要检查哪个tableView 正在调用您的委托方法。假设您的 viewControllersearch fetchedresultscontrollernormal fetchedresultscontroller 以及 searchdelegate tableView正常 tableView

所以你需要一些东西,比如

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{

    If (tableView == searchTableView) {
      // do stuff to searchTableView

    } else  {
      // do stuff to normalTableView

    }
}

或者在 fetchedResultsController 委托方法中(我很确定这将是你在同一个 tableView 上获得两个调用的地方)类似 p>

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

    If (controller == searchFetchedResultsController) {
       // do stuff to searchTableview

    } else {
       // do stuff to normaltableview

    }
}

【讨论】:

  • 我确实检查了显示哪个表视图以使用正确的 FRC。即使我从主表视图中删除(在搜索并返回主表并删除单元格之后)也会发生此问题
  • 你确定你没有从两个表视图中错误地删除它,你应该能够从搜索表视图中删除它,它应该会自动从主表视图中删除。
  • 请记住,当您删除对象时,两个 fetchedresultscontroller 都会调用它们的委托方法,这似乎正是您上面描述的内容,但是您的代码没有检查哪个 fetchedresultscontroller 正在调用委托方法。
  • 上面列出的 didChangeObject 方法没有检查哪个 fetchedResultsController 正在调用委托方法
  • 哇,我确信我做得对。我检查了搜索栏控制器是否处于活动状态,而不是“如果(控制器 == searchFetchedResultsController)”。大错特错。似乎同一个 tableview 被调用了两次!如您所描述的,一个用于主表视图,另一个用于搜索表视图。你最后的 cmets 帮助我达到了这个事实。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-14
相关资源
最近更新 更多