xsee
xsee

Reputation: 1163

UITableView bouncing back to the top of a section when calling reloadRowsAtIndexPaths

When a user taps a button in one of my rows I am updating the underlying model for that row and then calling reloadRowsAtIndexPaths for the given row (i.e. single row reload).

- (IBAction)handleCompleteTouchEvent:(UIButton *)sender {
    NSIndexPath *indexPath = [self.tableView indexPathForView:sender];
    id item = [self dataForIndexPath:indexPath];

    if ([item respondsToSelector:@selector(completed)]) {
        // toogle completed value
        BOOL completed = ![[item valueForKey:@"completed"] boolValue];
        [item setValue:[NSNumber numberWithBool:completed] forKey:@"completed"];

        [self.tableView beginUpdates];
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];
    }
}

The problem is that the table view bounces back to the top of the section after making this call. How can I prevent this from occurring and keep the scroll position where it is?

Upvotes: 38

Views: 12402

Answers (10)

Ethan Allen
Ethan Allen

Reputation: 14835

This solved this issue for me with Swift 5 / iOS 14:

UIView.performWithoutAnimation({
   self.tableView.reloadRows(at: [self.idxTouched], with: .none)
})

Upvotes: 0

Giorgi Shavgulidze
Giorgi Shavgulidze

Reputation: 51

This did the trick for me.

UIView.setAnimationsEnabled(false)
tableView.reloadRows(at: [...], with: .none)
UIView.setAnimationsEnabled(true)

Upvotes: 5

Tanvir Singh
Tanvir Singh

Reputation: 141

Swift 4.2

This can work anywhere you want to remove animation. During reload of table , table section or any row

 UIView.performWithoutAnimation({
                cell.configureSelection(isSelected: true)
                tableView.reloadSections([1], with: .none)
                tableView.allowsSelection =  false

            })

Upvotes: 4

devjme
devjme

Reputation: 720

I had the same issue. I ended up just calling tableView.reloadData() instead after updating my data / cell and it didn't bounce back to the top / data was updated in place - FYI

Upvotes: 0

Michael McKenna
Michael McKenna

Reputation: 794

To build off xsee's answer - I had set the Estimate in interface builder to "automatic". I changed this to another number and it started working. I kept Row Height to automatic.

Upvotes: 0

devdoe
devdoe

Reputation: 4325

If you know the minimum height of your cell, you must specify that while setting estimatedRowHeight. I was setting it to 1 as I have read before somewhere that any value above 0 would suffice the purpose, but it was the culprit.

When I set it to 44, which was the minimum height my cell could have, all went fine.

Dynamic heights were also working fine, no issues with this fix.

Upvotes: 0

zeeawan
zeeawan

Reputation: 6895

In my similar case, I had to tweak the implementation of method

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
...
}

where my heights were being reset. So, instead of resetting height for every cell I updated only the effected cells for reloadRowsAtIndexPaths call.

Upvotes: 1

Vlad
Vlad

Reputation: 5927

It may be possible to put the cell in it's own section and call reload section:

[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];

this appears to fix the issue partially. Not the cleanest way but it may work for you.

Upvotes: 1

xsee
xsee

Reputation: 1163

Ah Ha! I found the problem and am going to answer my own question for the poor soul who runs into this issue in the future.

All of my cells have variable height so I was using the new iOS7 method in UITableViewDelegate thinking it might speed up render time (not that I really needed it):

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;

Anyway, implementing this method has the evil side effect of causing the table to bounce to the top of the section when calling:

[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

To solve the bounce problem I just removed the override of that estimatedHeightForRowAtIndexPath method and now everything works as it should. Happy at last.

Upvotes: 49

strwils
strwils

Reputation: 697

You should be able to do what you are trying to do by changing the cell contents directly. For example, if you are using the base UITableViewCell class and the data in your model is a NSString which you show in the table view cell, you can do the following (after you change your data model) instead of calling reloadRowsAtIndexPaths:

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *label = [cell textLabel];
label.text = @"New Value";

If you are using a custom subclass of UITableViewCell, it's roughly the same except for accessing the views of the cell will need to be done through the contentView property.

Upvotes: 3

Related Questions