【问题标题】:Simultaneous move and update of UITableViewCell and NSFetchedResultsControllerUITableViewCell 和 NSFetchedResultsController 的同时移动和更新
【发布时间】:2013-01-16 08:47:10
【问题描述】:

我有一个包含 1 节和 2 行的简单表格视图。我使用NSFetchedResultsController 来保持表格与CoreData 同步。我对 CD 中的一行进行了更改,这会触发要更新和移动的表格视图单元格。问题是当cellForRowAtIndexPath: 在NSFetchedResultsChangeUpdate 期间被调用时,会返回错误的单元格(这很有意义,因为单元格尚未移动)。因此,错误的单元格会使用新更新的数据进行更新。之后处理NSFetchedResultsChangeMove 消息,以便单元格交换位置(单元格的内容都不会更新,因为它只是一个移动调用)。结果是两个单元格都反映了来自新更新的 CD 实体的数据。重新加载表可以解决问题。我正在运行 iOS 6。 换句话说,如果索引 0 处的单元格代表实体 A,索引 1 代表实体 B,并且我将实体 A 更新为 A',以使 2 个单元格的顺序相反,结果是我看到 0:A' 1:A当我期望0:B,1:A'时。

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

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

        case NSFetchedResultsChangeUpdate:
//the wrong cell is updated here
            [self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
//this code produces errors too
            //[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            //[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

【问题讨论】:

    标签: ios uitableview ios6 nsfetchedresultscontroller


    【解决方案1】:

    解决办法是:

    [self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:newIndexPath ? newIndexPath : indexPath];
    

    使用更新期间提供的新索引路径。然后使用删除和插入而不是移动。我仍然想知道其他人是否有任何意见。

    【讨论】:

    【解决方案2】:

    我建议你看看这个blogpost

    修复错误很容易。只需依赖我上面描述的 UITableView 的行为,并将对 configureCell:atIndexPath: 的调用替换为 reloadRowsAtIndexPaths:withRowAnimation: 方法,它会自动做正确的事情:

    case NSFetchedResultsChangeUpdate:
       [tableView reloadRowsAtIndexPaths:@[indexPath]  withRowAnimation:UITableViewRowAnimationAutomatic];
       break;
    

    【讨论】:

    • 无论如何,如果我使用 moveRowAtIndexPath 而不是 delete+insert 组合,我不会收到更新事件。知道可能是什么原因吗?
    【解决方案3】:

    调用configureCell时,根据传入的对象查找indexPath,此时indexPath和newIndexPath都不可靠。例如:

    case NSFetchedResultsChangeUpdate:
        myPath = [controller indexPathForObject:anObject];
        if (myPath) {
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:myPath];
        }
    
        break;
    

    【讨论】:

      【解决方案4】:

      唯一对我有用的解决方案:Thomas Worrall's Blog: Reordering rows in a UITableView with Core Data

      首先,必须在你的 NSManagedObject 中创建一个属性来挂起你的对象的最后一个订单。

      @interface MyEntity : NSManagedObject
      
      @property (nonatomic, retain) NSNumber * lastOrder;
      
      @end
      

      然后,在 ViewController.m 中声明一个属性:

      @interface ViewController ()
      
      @property (nonatomic) BOOL isReordering;
      
      @end
      

      在方法 tableView:moveRowAtIndexPath:toIndexPath: 中,管理临时数组中的更改:

      - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
      {
          _isReordering = YES;
      
          NSMutableArray *arrayNewOrder = [[_fetchedResultsController fetchedObjects] mutableCopy];
          MyEntity *myManagedObject = [arrayNewOrder objectAtIndex:fromIndexPath.row];
          [arrayNewOrder removeObjectAtIndex:fromIndexPath.row];
          [arrayNewOrder insertObject:myManagedObject atIndex:toIndexPath.row];
      
          for (int i=0; i < [arrayNewOrder count]; i++)
          {
              myManagedObject = [arrayNewOrder objectAtIndex:i];
              myManagedObject.lastOrder = [NSNumber numberWithInt:i];
          }
      
          _isReordering = NO;
          NSError *error;
          BOOL success = [self.fetchController performFetch:&error];
          if (!success)
          {
              // Handle error
          }
      
          success = [[self managedObjectContext] save:&error];
          if (!success)
          {
              // Handle error
          }
      }
      

      Thomas 解释了执行获取和保存上下文:

      我不完全确定为什么需要先执行提取,但是 当我这样做时,它修复了很多疯狂的错误!

      最后一个技巧是处理 NSFetchedResultsController 委托方法 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:,特别是在 NSFetchedResultsChangeMove:

      case NSFetchedResultsChangeMove:
      {
          if (!_isReordering)
          {
              [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                               withRowAnimation:UITableViewRowAnimationFade];
              [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                               withRowAnimation:UITableViewRowAnimationFade];
          }
      
          break;
      }
      

      它对我有用!希望对你有帮助!

      【讨论】:

      • 是FRC中的bug,我们不能在tableview上使用moveRow并强制删除+插入?
      猜你喜欢
      • 2011-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多