【问题标题】:UITableView powered by FetchedResultsController with UITableViewAutomaticDimension - Cells move when table is reloadedUITableView 由 FetchedResultsController 和 UITableViewAutomaticDimension 提供支持 - 重新加载表格时单元格移动
【发布时间】:2015-07-18 09:12:51
【问题描述】:

当前设置:

具有自动计算高度的 TableView:

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

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

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

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

问题:

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

其他信息

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

示例

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

【问题讨论】:

    标签: ios objective-c uitableview autolayout


    【解决方案1】:

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

    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. 如果您没有为任何estimated… 属性提供值并且没有实现任何tableViewController:estimated… 方法,则UITableView 通过调用适当的委托方法(例如@)向其委托询问页眉、页脚和行的确切高度987654331@。如果您的代理报告的对象数量和它们的高度与重新加载之前相同,那么您将不会看到任何表格元素的位置和大小的任何视觉变化。当您的表格应该显示大量行时,这种“海峡”行为的不利因素变得明显,比如说 50000。UITableView 向其委托询问这 50000 行中每一行的高度,您必须通过测量每个对应的文本来自己计算它对象,或者当使用UITableViewAutomaticDimension UITableView 进行相同的测量时,向其代表询问填充了文本的单元格。相信我,它很慢。每次重新加载都会导致界面冻结几秒钟。
      2. 如果您为 UITableView 提供了估计的高度,那么它将仅向其委托询问当前可见对象的高度。 [0…220] 垂直范围内的对象通过使用estimatedRowHeight-tableView:estimatedHeightForRowAtIndexPath: 中提供的值用于行以及通过相应的方法用于节页眉和页脚进行计数。通过将estimatedRowHeight 设置为 60,您告诉 UITableView 跳过三行 (60 * 3 = 180) 并将第 4 行与顶部可见边缘的偏移量设置为 -40。因此视觉上“跳跃”了 40 个像素。

    这里的“正确”解决方案是不要调用reloadData。只为更改的对象重新加载行,使用-reloadRowsAtIndexPaths:withRowAnimation:。如果是 NSFetchedResultsController + UITableView 使用这个classic scheme

    【讨论】:

    • 很好的答案,谢谢。非常详细,您是如何发现这些内部运作的?因为从文档中并不明显。通过仅重新加载一个单元格,它可以解决问题。它实际上是一个可折叠的 UITableView 实现,所以我有一个方法来配置所需的单元格,而不是 -reloadRowsAtIndexPaths:withRowAnimation: 的系统方法
    • @bteapot:我也有类似的问题。但它发生在我分页然后调用 tableview 重新加载时。重新加载后,当我滚动到时,会发生上述跳转。向下滚动时没有问题。请评论解决方案。
    • @krishnanunni 你已经用你的表视图实现了tableView:estimatedHeightFor…,或者estimatedRowHeight/estimatedSection…?你真的需要分页吗?值得仅使用大行数来实现,这不应该是分页的问题。尽量不要使用估计的高度,看看会发生什么。
    • @bteapot:是的!我已经实现了 tableView:estimatedHeight。当我第一次加载时,单元格高度都是错误的。稍作延迟后,它会自行调整。但这会导致非常糟糕的用户体验。当我实现 tableview:estimatedHeight 时,这个问题就解决了。我的估计高度委托方法如下: - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { if (IOS8_OR_LATER) { return UITableViewAutomaticDimension; } 其他 { 返回 710; } }
    • @bteapot :在 WillDisplayCell 中缓存计算出的高度并在estimatedHeightForRow 中使用它对我有用。还发现这是 iOS 8 中的一个错误。在 iOS 9 中没有问题。发现如下:github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/issues/17openradar.appspot.com/19581195
    猜你喜欢
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多