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

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.


I had this issue too and used a solution from SO which I am unable to find right now. If I do, I will add the link. Here's the solution:

The issue is with table view not having the right estimate for height of rows. To fix it, cache the height initially in willDisplayCell and next time use that height.

Code

In viewDidLoad:

heightAtIndexPath = [NSMutableDictionary new];


- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *height = @(cell.frame.size.height);
    [heightAtIndexPath setObject:height forKey:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if([heightAtIndexPath objectForKey:indexPath]) {
        return [[heightAtIndexPath objectForKey:indexPath] floatValue];
    } else {
        return UITableViewAutomaticDimension;
    }
}

In iOS 13.5.1:

I have a tableView which contains 4 types of cells , all off them having different and dynamic heights. I have followed the accepted solution here, but to solve jumping effect I need to add more when reloading table view:

From accepted answer I have added below code: Declare this variable

var allCellHeights = [IndexPath : CGFloat]()

Then add 2 methods:

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
     self.allCellHeights[indexPath] = cell.frame.size.height
 }
 func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
     return self.allCellHeights[indexPath] ?? UITableView.automaticDimension
 }

Now the extra code I have to add when reloading tableview:

let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)

also check this answer : reloadData() of UITableView with Dynamic cell heights causes jumpy scrolling