UITableView意外地与beginUpdates()/ endUpdates()/ performBatchUpdates()反弹 [英] UITableView unexpectedly bounces with beginUpdates()/endUpdates()/performBatchUpdates()

查看:91
本文介绍了UITableView意外地与beginUpdates()/ endUpdates()/ performBatchUpdates()反弹的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这里,我有一个非常简单的 UITableViewController / NSFetchedResultsController 案例。它来自Xcode Master-Detail App示例代码,因此易于重现。
我有一个CoreData模型,其中1个实体具有1个字符串属性。它显示在 UITableViewController 中。我使用 .subtitle 系统单元格类型。
通过选择一行,我只是更新了字符串属性。

所以我的问题是,当我添加足够多的行以供tableview滚动时(在具有导航栏的iPhone 5s上为10-11行),然后向下滚动并选择任意行,tableview就会弹起和向下。

如果行较少(少于10行)或行较多(12行以上),则行为正常。

所以这似乎是在滚动视图的限制下发生了问题。
如果我不使用 beginUpdates() / endUpdates(),问题就消失了,但是我失去了他们的优势。

这里是发生了什么的视频链接

I have a pretty straight forward UITableViewController /NSFetchedResultsController case here. It's from Xcode Master-Detail App sample code, So easy to reproduce.
I have a CoreData Model with 1 Entity with 1 String Attribute. It's displayed in a UITableViewController. I use .subtitle system cell type. By selecting a row, I simply update the String Attribute.
So my problem is, when I add just enough rows for the tableview to scroll (10-11 rows on a iPhone 5s with navbar), and I scroll down, and select any row, the tableview bounces up and down.
If there is less rows (less than 10 rows), or more rows (12 rows and more), the behaviour is normal.
So It seems to be at the limit of the scroll view the problem happens. If I don't use beginUpdates()/endUpdates(), the problem goes away, but I loose their advantages.
Here is a video link of what happens

class TableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

  var managedObjectContext: NSManagedObjectContext? = nil

  @objc func insertNewObject(_ sender: Any) {
    let context = self.fetchedResultsController.managedObjectContext
    let newEvent = Event(context: context)
    newEvent.aString = "a String"
    try? context.save()
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
    navigationItem.rightBarButtonItem = addButton
  }

  override func numberOfSections(in tableView: UITableView) -> Int {
    return fetchedResultsController.sections?.count ?? 0
  }

  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionInfo = fetchedResultsController.sections![section]
    return sectionInfo.numberOfObjects
  }

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    let event = fetchedResultsController.object(at: indexPath)
    configureCell(cell, withEvent: event)
    return cell
  }

  func configureCell(_ cell: UITableViewCell, withEvent event: Event) {
    cell.textLabel?.text = event.aString
    cell.detailTextLabel?.text = event.aString
  }

  override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    let event: Event = self.fetchedResultsController.object(at: indexPath)
    event.aString = event.aString! + ""
  }

  override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
  }

  override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
      let context = fetchedResultsController.managedObjectContext
      context.delete(fetchedResultsController.object(at: indexPath))
      try? context.save()
    }
  }

  var fetchedResultsController: NSFetchedResultsController<Event> {
    if _fetchedResultsController != nil {
      return _fetchedResultsController!
    }

    let fetchRequest: NSFetchRequest<Event> = Event.fetchRequest()
    fetchRequest.fetchBatchSize = 20
    let sortDescriptor = NSSortDescriptor(keyPath: \Event.aString, ascending: false)
    fetchRequest.sortDescriptors = [sortDescriptor]

    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController
    try? _fetchedResultsController!.performFetch()

    return _fetchedResultsController!
  }

  var _fetchedResultsController: NSFetchedResultsController<Event>? = nil

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

  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    switch type {
    case .insert:
      tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic)
    case .delete:
      tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic)
    default:
      return
    }
  }

  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
    case .insert:
      tableView.insertRows(at: [newIndexPath!], with: .automatic)
    case .delete:
      tableView.deleteRows(at: [indexPath!], with: .automatic)
    case .update:
      configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Event)
    case .move:
      configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Event)
      tableView.moveRow(at: indexPath!, to: newIndexPath!)
    }
  }

  func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
  }
}


推荐答案

我遇到了同样的问题,发现实现 estimatedHeightFor ... 方法为我解决了此问题。问题似乎源于在表格中使用自动单元格高度,而不是每行明确定义高度。

I was running into the same problem, and I found that implementing the estimatedHeightFor... methods fixed the issue for me. The issue seems to stem from using automatic cell heights in the table instead of explicitly defined heights per row.

我正在使用的表格既有行又有节页眉/页脚,所以我需要定义行和标题的估计高度,这解决了批量更新期间奇怪的弹跳动画。

The table I'm using has both rows and section header/footers, so I needed to define estimated heights for both rows and headers, and this solved the strange bouncing animation during batch updates.

请注意,返回0的节标题高度将使用表格视图的默认值可能是 UITableViewAutomaticDimension ,因此返回一个估计高度的正数。

Note that returning 0 for section header heights will use the table view's default value which is likely UITableViewAutomaticDimension, so return a positive number for the estimated heights.

我的代码在Obj-C中:

My code in Obj-C:

-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 44; // anything except 0 or UITableViewAutomaticDimension
}

-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section{
    return 18; // anything except 0 or UITableViewAutomaticDimension
}

-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section{
    return 18; // anything except 0 or UITableViewAutomaticDimension
}

这篇关于UITableView意外地与beginUpdates()/ endUpdates()/ performBatchUpdates()反弹的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆