Saving scroll position after reload in tableView

I have a tableView. When the user deletes one of the records, I check its property and based on that, I filter the results and only show similar results - so I need to update the tableView.

The problem is that the user can scroll up / down the tableView. I need to scroll the tableView so that the cell is exactly the same UITableViewScrollPosition

as it was before the update.

Obviously I am saving the last used item

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    _lastSelectedItem = [self itemForIndexPath:indexPath];


Then after reloading the tableView:

NSIndexPath *indexPath = [self indexPathForItem:_lastSelectedItem];
if (_tableView.numberOfSections > indexPath.section && [_tableView numberOfRowsInSection:indexPath.section] > indexPath.row) {
    [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
    _lastSelectedItem = nil;


It would be nice, but ... the user was unable to finish with UITableViewScrollPositionMiddle

. He could have finished scrolling at UITableViewScrollPositionTop

or UITableViewScrollPositionBottom

or even somewhere in between.

- change -

Calculating with offset is also problematic, as offset is the difference between the start view and the scroll bar of the top table. I'm not interested in this yet: /.


source to share

1 answer

Since my structure was quite complex, I ended up doing it like this (e.g. expandable sections, etc.) Please keep in mind that I am storing _lastSelectedItem

which is my object in the datasource as the indexPath will change after the update.

- (void)refresh {
    [self saveLastScrollPosition];
    [self reloadData]; // inside we reload data + reload tableView
    [self scrollToLastScrollPosition];

- (NSInteger)heightToMinYOfCellAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger sections = indexPath.section;
    NSInteger totalRows = 0;
    for (NSInteger section = 0; section < indexPath.section; section++) {
        totalRows += [self.tableView numberOfRowsInSection:section];
    totalRows += indexPath.row;

    return ((sections + 1) * kHeaderHeight + totalRows * kRowHeight);

- (void)saveLastScrollPosition {
    if (_lastSelectedItem) { // make sure we have that item selected
        NSIndexPath *indexPath = [self itemForItem:_lastSelectedItem];
        NSInteger aboveItemHeight = [self heightToMinYOfCellAtIndexPath:indexPath];
        CGFloat contentOffsetY = self.tableView.contentOffset.y;
        _previousOffset = aboveItemHeight - contentOffsetY;

- (void)scrollToLastScrollPostion {
    if (_lastSelectedItem) { // make sure we have that item selected
        NSIndexPath *indexPath = [self itemForItem:_lastSelectedItem];
        if (self.tableView.numberOfSections > indexPath.section && [self.tableView numberOfRowsInSection:indexPath.section] > indexPath.row) { // make sure the indexPath still exist after reload
            NSInteger aboveItemHeight = [self heightToMinYOfCellAtIndexPath:indexPath]; // height of items above selected index
            CGPoint offset = CGPointMake(0.f, aboveItemHeight - _previousOffset);
            // in case index should be higher: eg earlier item was at index (4,8) now is at (0,0)
            if (offset.y < 0) {
                offset.y = 0;
            [self.tableView setContentOffset:offset animated:NO]; // just jump, user shouldn't see this
            _previousOffset = 0; // forget the calculated values
            _lastSelectedItem = nil;




All Articles