UITableView scrolls to top when reloading cells with changing cell heights

I have a table view which contains a placeholders while it loads in images. When the image is loaded, I call reloadRowsAtIndexPaths:withRowAnimation:. At this point, the cell changes height, based on the size of the image. When that happens, I want the table view’s content offset to remain in place, and for the cells below to be pushed further down, as you might imagine.

The effect I’m getting instead is that the scroll view scrolls back to the top. I’m not sure why this is, and I can’t seem to prevent it. Putting beginUpdates() before and endUpdates()after the reloadRows line has no effect.

  • Proper way to optimize CSS 3 animations for iOS/Mobile Safari?
  • How to effectively process UITouches in a multitouch sequence
  • Create NSURL using AssetPath
  • How to change color of text stirngs inside UITextView in Swift3
  • Get a substring from an NSString until arriving to a specific word
  • Check type of class an NSData store?
  • I am using estimatedRowHeight, as is needed as my table view can potentially have hundreds of rows of different heights. I am also implementing tableView:heightForRowAtIndexPath:.

    EDIT: I’ve set up a demo project to test this, and admittedly I can’t get the demo project to reproduce this effect. I’ll keep working at it.

    4 Solutions Collect From Internet About “UITableView scrolls to top when reloading cells with changing cell heights”

    Always update the UI on the main thread. So just place

    [self.tableView reloadData];
    

    inside a main thread:

    dispatch_async(dispatch_get_main_queue(), ^{
         //UI Updating code here.
         [self.tableView reloadData];
    });
    

    It’s an issue with the estimatedRowHeight.

    The more the estimatedRowHeight differs from the actual height, the more the table may jump when it is reloaded, especially the further down it has been scrolled. This is because the table’s estimated size radically differs from its actual size, forcing the table to adjust its content size and offset.

    The easiest workaround is to use a really accurate estimate. If the height per row varies greatly, determine the median height for a row, and use that as the estimate.

    I had the same problem and decide it by this way: save heights of cells when they loads and give exact value in tableView:estimatedHeightForRowAtIndexPath:

    // declare cellHeightsDictionary
    NSMutableDictionary *cellHeightsDictionary;
    
    // save height
    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
        [cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:indexPath];
    }
    
    // give exact height value
    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
        NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
        if (height) return height.doubleValue;
        return UITableViewAutomaticDimension;
    }
    

    I was seeing this, and the fix that worked for me was to choose an estimated row height that is the smallest of the possible rows. It had originally been set to the largest possible row height when the unintended scrolling was happening. I am just using the single tableView.estimatedRowHeight property, not the delegate method.