9

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.

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.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Andrew
  • 7,693
  • 11
  • 43
  • 81
  • Can you post the code your using for getting the tableview's content offset and setting where you would like the tableview's focus to remain? – MSU_Bulldog Sep 02 '15 at 20:52

4 Answers4

29

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.

  • In my test, I tried with a very small estimate, and a very large estimate, but neither had the effect of scrolling unwantedly. – Andrew Sep 02 '15 at 22:43
  • This is a pretty good answer. Thank you. The estimatedHeight should not be a random number but close to what you think the height is going to be. – darwindeeds Oct 09 '15 at 20:44
  • I used a resizing cell in the estimated size (which kind of defeats the purpose of the UITableViewAutomaticDimension I guess?). And it solved the problem for me. Its because my cells vary a lot in size (According to what the user types in the UITextViews embedded in there) – DatForis Jan 27 '17 at 12:29
10

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];
});
Baig
  • 4,737
  • 1
  • 32
  • 47
6

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;
}
Igor
  • 12,165
  • 4
  • 57
  • 73
  • In my case i had `self.tableView.estimatedRowHeight = 44;` in superclass. Setting it to zero in current class, where i have fixed row heights fixed the problem. – Nike Kov Nov 08 '16 at 07:44
0

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.

jamone
  • 17,253
  • 17
  • 63
  • 98