Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView with dynamic cell heights jumping when scrolling up after reloading cell

I have a table view with the potential for each cell to have its own height, thus isn't suitable to use rowHeight. Instead, right now I'm using let indexSet = NSIndexSet(index: 10), and self.tableView.estimatedRowHeight = 75. This means that it calls the sizeThatFits function on the cell, to determine its' height. This all works well.

The problem is then when you reload a cell that's onscreen. Scrolling down to show cell 10, for example, then reloading cell 10, works fine. But when you begin to scroll back up, past the cells you've already seen, it reverts to the estimatedRowHeight for each cell, completely disregarding sizeThatFits, and so jumps around as you scroll. It'd be impossible for me to give an accurate or 'good enough' estimatedRowHeight so that this jumping wouldn't be noticeable, as my cells will be able to display either a line of text, or a full image - a big difference in size.

I've shown this effect here:

https://vid.me/edgW

I've made many different attempts at this, using a mixture of heightForRowAtIndexPath, estimatedHeightForRowAtIndexPath.. etc.. I've tried various pieces of advice on StackOverflow. Nothing seems to work.

I've attached a very simple sample project, where you can try it for yourself:

https://www.dropbox.com/s/8f1rvkx9k23q6c1/tableviewtest.zip?dl=0

  1. Run the project.
  2. Scroll until cell 10 is in view.
  3. Wait up to 5 seconds for the cell to reload (it becomes purple).
  4. Scroll up.

Worth noting - this does not happen if the cell is not in view when it's reloaded. If it's either above or below the current scroll point, everything works as expected.

like image 863
Andrew Avatar asked Sep 07 '15 14:09

Andrew


1 Answers

This behavior appears to be a bug, if for no other reason than it's no longer reproducible on iOS 9. I'm sure that's not much consolation.

The issue primarily derives from having an inaccurate estimate, like @NickCatib said. The best you can do on iOS 8 is to improve the estimation. A technique many have recommended is to cache heights in willDisplayCell and use them on subsequent calls to estimatedRowHeightAtIndexPath.

You might be able to mitigate the behavior by not doing anything to get UITableView to discard its caches, like by modifying the content in a cell directly using cellForRowAtIndexPath rather than using reloading if it's onscreen. However, that won't help if you actually need to change the height of the cell.

I'm afraid to say the bug can't be easily be fixed within a table view, as you don't have control over the layout. The bug can be more easily worked around in a subclass UICollectionViewFlowLayout by changing the contentOffsetAdjustment during invalidation, although that might not be terribly easy.

like image 112
zwaldowski Avatar answered Sep 19 '22 05:09

zwaldowski