UITableView 由 FetchedResultsController 和 UITableViewAutomaticDimension 提供支持 - 重新加载表格时单元格移动 [英] UITableView powered by FetchedResultsController with UITableViewAutomaticDimension - Cells move when table is reloaded

查看:10
本文介绍了UITableView 由 FetchedResultsController 和 UITableViewAutomaticDimension 提供支持 - 重新加载表格时单元格移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前设置:

具有自动计算高度的 TableView:

TableView with automatically calculated heights:

self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension;
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 152.0;
self.tableView.estimatedSectionHeaderHeight = 50.0;

每当获取的结果控制器更新其数据时,表格视图就会重新加载:

Whenever the fetched results controller updates its data the tableview is reloaded:

 - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView reloadData]; 
}

使用 Xib 配置单元.第一个标签固定在单元格的顶部,后面的每个标签都固定在其上方标签的顶部,底部的标签固定在单元格的底部.

The cell is configured using a Xib. The first label is pinned to the top of the cell, each following label is pinned to the top of the label above it and the label at the bottom is pinned to the bottom of the cell.

问题:

每次我设置一个收藏夹"时表视图中项目的属性,获取的结果控制器被触发以重新加载表并更改滚动位置.我正在尝试修复的正是滚动位置的这种变化.

Each time i set a "Favourite" property on an item in the table view, the fetched results controller is fired to reload the table and the scroll position is changed. It is this change in the scroll position that i am trying to fix.

其他信息

如果我使用固定的单元格高度,它可以解决问题,但我需要 UITableViewAutomaticDimension,因为第一个标签可以跨越两行,其余标签可能存在也可能不存在.

If i use fixed cell heights it resolves the issue BUT i require UITableViewAutomaticDimension because the first label can wrap over two lines and the remaining labels may or may not be present.

示例

注意 - 当我选择 Fav 按钮时,它会在 Core data 中设置 fav 属性并重新加载表格.为什么桌子会跳来跳去?

Note - As i select the Fav button it sets the fav property in Core data and reloads the table. Why is the table jumping around?

推荐答案

它的发生是因为以下顺序:

It happens because of the following sequence:

  1. UITableView 已初始化并显示 5 个单元格.UITableView 知道每个单元格的高度.它通过调用 -tableView:heightForRowAtIndexPath: 方法在显示每个单元格之前询问其委托的确切高度.
  2. UITableView 从顶部滚动了 3 个单元格.众所周知,这个单元的高度恰好是 [60, 70, 90] = 220.UITableView 的 contentOffset.y 现在是 220.
  3. UITableView 被重新加载.它清除了所有关于细胞的知识.它现在仍然知道它的 contentOffset.y 是 220.
  4. UITableView 向其数据源询问一般指标 - 节数和每个节中的行数.
  5. UITableView 现在开始填充其内容.首先,它需要知道其内容的大小以正确调整其滚动指示器的大小和位置.它还需要知道哪些对象 - 表头、节头、行、节脚和表脚 - 它应该根据其当前的 bounds 显示,哪个位置也由 contentOffset.要开始放置可见对象,首先需要跳过不可见垂直范围 [0…220] 内的对象.

  1. UITableView initialized and showing 5 cells. Height of each of that cells is known to UITableView. It asks its delegate for exact height before displaying each cell by calling a method -tableView:heightForRowAtIndexPath:.
  2. UITableView scrolled exactly 3 cells from top. Heights of this cells are known to be exactly [60, 70, 90] = 220 summarily. UITableView's contentOffset.y is now 220.
  3. UITableView gets reloaded. It purges all its knowledge about cells. It now still knows its contentOffset.y which is 220.
  4. UITableView asking its data source about general metrics - number of sections and number of rows in each section.
  5. UITableView now beginning to fill its contents. First it needs to know size of its contents to correctly size and position its scroll indicators. It also needs to know which objects - table header, section headers, rows, section footers and table footer - it should display according to its current bounds, which position is also represented by contentOffset. To begin placing that visible objects it first needs to skip objects that falls in invisible vertical range of [0…220].

  1. 如果您没有为任何 estimated… 属性提供值并且没有实现任何 tableViewController:estimated… 方法,那么 UITableView 会询问其委托人确切的高度通过调用适当的委托方法(例如 -tableView:heightForRowAtIndexPath:)来控制页眉、页脚和行.如果您的代理报告的对象数量和它们的高度与重新加载之前相同,那么您将不会看到任何表格元素的位置和大小的任何视觉变化.当您的表格应该显示大量行时,这种海峡"行为的不利因素变得明显,比如说 50000.UITableView 向其委托询问这 50000 行中每一行的高度,您必须通过测量每个对应的文本来自己计算它对象,或者当使用 UITableViewAutomaticDimension UITableView 进行相同的测量时,向其代表询问填充了文本的单元格.相信我,它很慢.每次重新加载都会导致界面冻结几秒钟.
  2. 如果您为 UITableView 提供了估计的高度,那么它只会向其委托询问当前可见对象的高度.[0…220] 垂直范围内的对象通过使用 estimatedRowHeight-tableView:estimatedHeightForRowAtIndexPath: 中提供的值来计算行,并通过相应的方法用于节页眉和页脚.通过将 estimatedRowHeight 设置为 60,您可以告诉 UITableView 跳过三行 (60 * 3 = 180) 并将第 4 行与顶部可见边缘的偏移量设置为 -40.因此视觉上跳跃"了 40 个像素.
  1. If you haven't provided values for any of estimated… properties and haven't implemented any of tableViewController:estimated…methods then UITableView asks its delegate about exact height of headers, footers and rows by calling appropriate delegate methods such as -tableView:heightForRowAtIndexPath:. And if your delegate reports the same number of objects and the same heights for them as before reload, then you will not see any visual changes to position and size of any table elements. Downside of this "strait" behavior became obvious when your table should display large number of rows, lets say 50000. UITableView asks its delegate about height of each of this 50000 rows, and you have to calculate it yourself by measuring your text for each corresponding object, or when using UITableViewAutomaticDimension UITableView doing the same measuring itself, asking its delegate for cells filled with text. Believe me, it's slow. Each reload will cause a few seconds of interface freeze.
  2. If you have supplied UITableView with estimated heights, then it will ask its delegate only for heights of currently visible objects. Objects in vertical range of [0…220] are counted by using values provided in estimatedRowHeight or -tableView:estimatedHeightForRowAtIndexPath: for rows and by corresponding methods for section headers and footers. By setting estimatedRowHeight to 60, you telling UITableView to skip three rows (60 * 3 = 180) and to place row 4 with offset of -40 from top visible edge. Hence visual "jump" by 40 pixels up.

这里的正确"解决方案是不调用 reloadData.只为更改的对象重新加载行,使用 -reloadRowsAtIndexPaths:withRowAnimation:.如果 NSFetchedResultsController + UITableView 使用这个 经典方案.

A "right" solution here would be not to call reloadData. Reload rows only for changed objects instead, use -reloadRowsAtIndexPaths:withRowAnimation:. In case of NSFetchedResultsController + UITableView use this classic scheme.

这篇关于UITableView 由 FetchedResultsController 和 UITableViewAutomaticDimension 提供支持 - 重新加载表格时单元格移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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