UITableView Scrolls to Top on Cell Refresh

2019-03-13 09:33发布

I have a UITableView with a cell that is dynamically sized to fit a UITextView inside it. Whenever a key is typed, the cell checks to see if the calculated height has increased, as with a newline, so it can tell the table the the cell's height needs to be recalculated. I do that with this code.

- (void) createNoteCellViewDidUpdate: (CreateNoteCell*) cell {

    CGFloat newHeight = [self tableView:self.tableView heightForRowAtIndexPath:CreateNoteCellIndexPath];
    if (newHeight != CGRectGetHeight(cell.contentView.frame)) {
        [self.tableView beginUpdates];
        [self.tableView endUpdates]; // <- HERE IS WHERE THE CONTENT OFFSET CHANGES
    }
}

The resize and table bounds are as expected. But instead of just animating the change in height, it also scrolls to the top. This can be seen here, when pressing the return key.

table scrolling on cell resize

By Key Value Observing, I've found out that the scroll view contentOffset is being set when the scroll view's contentSize changes. And that the contentSize changes many times when the endUpdates method is called. When it changes it goes from a normal height, to a rather large height, then back to a normal height. I wonder if it's going to top because of that. I don't know where to go from here.

4条回答
Melony?
2楼-- · 2019-03-13 10:16

More simplified solution (add Notification observer when textView is changed), Swift-version. Main trick is using UIView.setAnimationsEnabled.

func textViewDidChanged(notification: NSNotification) {
    UIView.setAnimationsEnabled(false)

    let contentOffset = self.tableView.contentOffset

    tableView.beginUpdates()
    tableView.endUpdates()

    tableView.contentOffset = contentOffset

    UIView.setAnimationsEnabled(true)
}
查看更多
冷血范
3楼-- · 2019-03-13 10:21

Here is my UITableView extension that works perfectly. In Swift 2.1. Based on the accepted answer.

extension UITableView
{
    func reloadDataAnimatedKeepingOffset()
    {
        let offset = contentOffset
        UIView.setAnimationsEnabled(false)
        beginUpdates()
        endUpdates()
        UIView.setAnimationsEnabled(true)
        layoutIfNeeded()
        contentOffset = offset
    }
}
查看更多
不美不萌又怎样
4楼-- · 2019-03-13 10:26

Wow, Josh Gafni's answer helped a lot; he deserves an upvote. My solution is based off his. This still feels like a hack, so if anyone still has a better way to do this, I'm still very interested to hear it.

First I save the content offset, then I start the update.

CGPoint offset = self.tableView.contentOffset;
[self.tableView beginUpdates];
[self.tableView endUpdates];

This animates the cell's height change and adjusts all the frames to suit the constraints.

Next I remove all animations from the tableView's layer, so the animation to the top stops. Then I reset the contentOffst to what it was before.

[self.tableView.layer removeAllAnimations];
[self.tableView setContentOffset:offset animated:NO];

Finally, I let the tableView handle the animated scroll down so the bottom of the cell is right above the keyboard.

[self.tableView scrollToRowAtIndexPath:CreateNoteCellIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

And voila, it works pretty well.

fixed cell resize

查看更多
Ridiculous、
5楼-- · 2019-03-13 10:31

Just a guess...

Save the contentOffset of the uitableview before any changes. Then set it again after.

CGFloat contentOffset = self.tableView.contentOffset;

and then after the changes

self.tableView.contentOffset = contentOffset;
查看更多
登录 后发表回答