ios - UITableView 使用 NSFetchedResultsController 和 CoreData 意外反弹到 beginUpdates()/endUpdates()/performBatchUpdates()

标签 ios swift uitableview core-data nsfetchedresultscontroller

UITableView 意外反弹 beginUpdates()/endUpdates()/performBatchUpdates() 使用 NSFetchedResultsController 和 CoreData 当行数填满 View 时。 重现它非常简单。 - 从主从应用程序模板(使用 CoreData)创建一个新项目。 - 在 Storyboard 中,删除“showDetail”转场。 (我们不需要详细 View ) - 在 MasterViewController 中,将 segue func prepare() 替换为:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {  
    let event = fetchedResultsController.object(at: indexPath)  
    let timestamp = event.timestamp  
    event.timestamp = timestamp // The idea is to simply update the Event entity.  
  }

启动应用程序(在 iOS 设备或模拟器中),并添加足够的行来填充 View (在 iPhone SE 中,它有 11 行)。 向下 ScrollView ,然后选择任意行。 View 将快速上下弹跳。 这是错误还是代码有问题?

最佳答案

好的,我可能找到了解决方案,请告诉我你们的想法。 这个想法是在 performBatchUpdates 中处理 insert/delete/move 并将 update 排除在外。 所以我创建了这个枚举和属性:

enum FetchedResultsChange<Object> {
  case insert(IndexPath)
  case delete(IndexPath)
  case move(IndexPath, IndexPath, Object)
}
var fetchedResultsChanges: [FetchedResultsChange<Event>] = []

controllerWillChangeContent 变为空:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {}

didChange 变成:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
    case .insert:
      self.fetchedResultsChanges.append(.insert(newIndexPath!))
    case .delete:
      self.fetchedResultsChanges.append(.delete(indexPath!))
    case .update:
      configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Event) // So this stays untouched.
    case .move:
      self.fetchedResultsChanges.append(.move(indexPath!, newIndexPath!, anObject as! Event))
    }
  }

controllerDidChangeContent 变成:

  func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    guard self.fetchedResultsChanges.count > 0 else { return }

    tableView.performBatchUpdates({
      repeat {
        let change = self.fetchedResultsChanges.removeFirst()
        switch change {
        case .insert(let newIndexPath):
          tableView.insertRows(at: [newIndexPath], with: .fade)
        case .delete(let indexPath):
          tableView.deleteRows(at: [indexPath], with: .fade)
        case .move(let indexPath, let newIndexPath, let event):
          configureCell(tableView.cellForRow(at: indexPath)!, withEvent: event)
          tableView.moveRow(at: indexPath, to: newIndexPath)
        }
      } while self.fetchedResultsChanges.count > 0
    }, completion: nil)
  }

那你怎么看?

关于ios - UITableView 使用 NSFetchedResultsController 和 CoreData 意外反弹到 beginUpdates()/endUpdates()/performBatchUpdates(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50981504/

相关文章:

ios - AnnotationView 在 calloutVIew 上重叠

ios - 通过自定义 init 方法从 Storyboard 加载 UIViewController

ios - viewContext 未保存在数据库中 (app.sqlite)

ios - 用于注释的 Swift 不同图像

ios - 如何在 Swift 中访问特定 GameViewController 实例的变量值

python - 高效刷新 TableView

iphone - CAGradientLayer 紫色而不是红色

ios - 如何在 navigationItems(rightBarButtonItems 和 leftBarButtonItems)中有效地搜索 viewWithTag

ios - 选择表格单元格时应用程序崩溃 Swift

objective-c - iOS UITableView : Custom sections using information from a JSON object, 不只是计算它