UITableView的搭载FetchedResultsController与UITableViewAutomaticDimension - 细胞移动表时重新加载 [英] UITableView powered by FetchedResultsController with UITableViewAutomaticDimension - Cells move when table is reloaded

查看:350
本文介绍了UITableView的搭载FetchedResultsController与UITableViewAutomaticDimension - 细胞移动表时重新加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前设置:

用的TableView自动计算高度:

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

每当读取的结果控制器更新其数据的tableview重新加载:

   - (无效)controllerDidChangeContent:(NSFetchedResultsController *)控制器{
    [self.tableView reloadData];
}

的电池是使用XIB配置。第一个标签被固定在电池的顶部,每个下面的标签被固定到它上面的标签,并在底部的标签的顶部被固定到单元的底部。

的问题:

每一次我树立了收藏财产在表视图中的项目,所取得的成果控制器发射重新加载表和滚动位置被改变。这是我试图修复滚动位置的这种变化。

其他信息

如果我用固定单元格高度就解决了问题,但我需要UITableViewAutomaticDimension,因为第一个标签可以换了两行,剩下的标签可能会或可能不会是present。

示例

请注意 - 正如我选择最爱按钮,它设置在核心数据的最爱财产和重新加载表。为什么表跳来跳去?


解决方案

这是因为下列顺序的:


  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现在开始,以填补其内容。首先,它需要知道它的内容来正确尺寸大小和定位其滚动指示器。它还需要知道哪些对象 - 表头,节头,行,部分页脚和表页脚 - 它应该根据当前的界限显示,哪个位置也重新$通过 contentOffset psented p $。首先把它首先需要跳过,在看不见的垂直范围落在对象可见对象[0 ... 220]。


    1. 如果您还没有任何估计... 属性提供的值,并没有任何实施的tableViewController:估计... 方法,然后UITableView的通过调用相应的委托方法,如 -tableView要求其有关的页眉,页脚和行确切高度的委托。如果你的委托报告相同数量的对象和他们同样的高度为重装前,那么你将不会看到任何可视更改任何表格元素的位置和大小。这种海峡的行为下行变得明显时,你的表应该显示大量的行,让说,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的情况下使用该<一个href=\"https://developer.apple.com/library/$p$prelease/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/index.html#//apple_ref/doc/uid/TP40008228-CH1-SW13\">classic方案。

Current set up:

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]; 
}

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.

The Issue:

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.

Additional Info

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.

Example

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 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. 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.

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天全站免登陆