How to keep UITableView contentoffset after calling -reloadData

CGPoint offset = [_table contentOffset];
[_table reloadData];
[_table setContentOffset:offset animated:NO];    //unuseful

//    __block UITableView *tableBlock = _table;
//    [self performBlock:^(id sender) {
//        [tableBlock setContentOffset:offset];
//    } afterDelay:2];

I know don't know of any delegate method which gets called after reloadData. And using afterDelay:2 which is kind of a hack may be too short or too long, so how can I implement it?


I was having trouble with this because I mess with cell sizing in my cellForRowAtIndexPath method. I noticed that the sizing information was off after doing reloadData, so I realized I needed to force it to layout immediately before setting the content offset back.

CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];

Calling reloadData on the tableView does not change the content offset. However, if you are using UITableViewAutomaticDimension which was introduced in iOS 8, you could have an issue.

While using UITableViewAutomaticDimension, one needs to write the delegate method tableView: estimatedHeightForRowAtIndexPath: and return UITableViewAutomaticDimension along with tableView: heightForRowAtIndexPath: which also returns the same.

For me, I had issues in iOS 8 while using this. It was because the method estimatedHeightForRowAtIndexPath: method was returning inaccurate values even though I was using UITableViewAutomaticDimension. It was problem with iOS 8 as there was no issue with iOS 9 devices.

I solved this problem by using a dictionary to store the value of the cell's height and returning it. This is what I did.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSNumber *key = @(indexPath.row);
    NSNumber *height = @(cell.frame.size.height);

    [self.cellHeightsDictionary setObject:height forKey:key];
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSNumber *key = @(indexPath.row);
    NSNumber *height = [self.cellHeightsDictionary objectForKey:key];

    if (height)
    {
        return height.doubleValue;
    }

    return UITableViewAutomaticDimension;
}

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

The check for whether height exists is for the first time page loads.