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.
UITableView powered by FetchedResultsController with UITableViewAutomaticDimension - Cells move when table is reloaded
It happens because of the following sequence:
- 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:
. - 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. - UITableView gets reloaded. It purges all its knowledge about cells. It now still knows its
contentOffset.y
which is 220. - UITableView asking its data source about general metrics - number of sections and number of rows in each section.
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 bycontentOffset
. To begin placing that visible objects it first needs to skip objects that falls in invisible vertical range of [0…220].- If you haven't provided values for any of
estimated…
properties and haven't implemented any oftableViewController: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 usingUITableViewAutomaticDimension
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. - 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 settingestimatedRowHeight
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.
- If you haven't provided values for any of
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
Load a .Tmx (Tiled Map) in Sprite Kit
Check If My iOS Application Is Updated
Swift How to Use Nstimer Background
Disable iOS Simulator 'Connect Hardware Keyboard' Programmatically
How to Convert String to Unicode(Utf-8) String in Swift
Swipe to Go Back Only Works on Edge of Screen
Multiple Uicollectionview in One Controller
iOS Are Methods Called by Delegates and Observers Executed on the Main Thread
Uitextview Linkable Label Accessibility Voice Over Issue
How to Add Text, Shape and Signature in Photo Markup with Pencil Kit
How to Find the Kind of Errors a Method May Throw and Catch Them in Swift
How to Set the Rootviewcontroller with Swift, iOS 7
Fonts Not Displaying in Interface Builder
Custom Scenekit Geometry in Swift on iOS Not Working But Equivalent Objective C Code Does