Error: Uitableview Jump to Top with Uitableviewautomaticdimension

Table view jumps when I try to scrollToRow with UITableViewAutomaticDimension. The fix also makes it jump

I resolved this problem when I added estimated row height for my table view:

var cellHeightsDictionary: [Int:CGFloat] = [:]

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeightsDictionary[indexPath.row] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeightsDictionary[indexPath.row] ?? 44.0
}

UITableViewAutomaticDimension not working until scroll

I ran into the same issue and found out that the accessory cell.accessoryType messes with this automatic resizing when it is not None, so it seems like a bug in Xcode at the moment.

But as @Blankarsch mentioned, calling reloadSections(..) helps to fix this issue if you need to have an accessory.

Accessory to "None" solved my issue

UITableView powered by FetchedResultsController with UITableViewAutomaticDimension - Cells move when table is reloaded

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 jumps up after begin/endUpdates when using UITableViewAutomaticDimension

Actually I found a nice method to fix this.. It drove me crazy but look:

So you

  • Have a table with expandable content
  • Wanna animate a cell's constraints (height for example)
  • And therefore you call tableView.beginUpdates() and tableView.endUpdates()

And soo the table jumps..

As others have said before, it is because updating the tableView makes the tableView Scroll

The solution?

Let's assume your code looks like this:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell.heightConstraint = 100
UIView.animate(withDuration: 0.15, animations: {

self.view.layoutIfNeeded()
self.tableView.beginUpdates()
self.tableView.endUpdates()
}, completion: nil)
}

Then to fix the jumping issue you have to save the current tableView scroll position until the tableView.endUpdates() being called.

Like this:

var currentScrollPos : CGFloat?

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Force the tableView to stay at scroll position until animation completes
if (currentScrollPos != nil){
tableView.setContentOffset(CGPoint(x: 0, y: currentScrollPos!), animated: false)
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell.heightConstraint = 100
UIView.animate(withDuration: 0.15, animations: {

self.currentScrollPos = self.tableView.contentOffset.y

self.view.layoutIfNeeded()
self.tableView.beginUpdates()
self.tableView.endUpdates()

self.currentScrollPos = nil
}, completion: nil)
}


Related Topics



Leave a reply



Submit