scrollToRowAtIndexPath:atScrollPosition causing table view to “jump”

痞子三分冷 提交于 2019-12-03 05:04:47

问题


My app has chat functionality and I'm feeding in new messages like this:

[self.tableView beginUpdates];
[messages addObject:msg];
[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:messages.count - 1 inSection:1]] withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:messages.count - 1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];

However, my table view "jumps" weirdly when I'm adding a new message (either sending and receiving, result is the same in both):

Why am I getting this weird "jump"?


回答1:


OK, I figured it out. As you say, the problem has to do with auto-sizing cells. I used two tricks to make things work (my code is in Swift, but it should be easy to translate back to ObjC):

1) Wait for the table animation to finish before taking further action. This can be done by enclosing the code that updates the table within a block between CATransaction.begin() and CATransaction.commit(). I set the completion block on CATransaction -- that code will run after the animation is finished.

2) Force the table view to render the cell before scrolling to the bottom. I do it by increasing the table's contentOffset by a small amount. That causes the newly inserted cell to get dequeued, and its height gets calculated. Once that scroll is done (I wait for it to finish using the method (1) above), I finally call tableView.scrollToRowAtIndexPath.

Here's the code:

override func viewDidLoad() {
    super.viewDidLoad()

    // Use auto-sizing for rows        
    tableView.estimatedRowHeight = 40
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.dataSource = self
}

func chatManager(chatManager: ChatManager, didAddMessage message: ChatMessage) {
    messages.append(message)

    let indexPathToInsert = NSIndexPath(forRow: messages.count-1, inSection: 0)

    CATransaction.begin()
    CATransaction.setCompletionBlock({ () -> Void in
        // This block runs after the animations between CATransaction.begin
        // and CATransaction.commit are finished.
        self.scrollToLastMessage()
    })

    tableView.beginUpdates()
    tableView.insertRowsAtIndexPaths([indexPathToInsert], withRowAnimation: .Bottom)
    tableView.endUpdates()

    CATransaction.commit()
}

func scrollToLastMessage() {
    let bottomRow = tableView.numberOfRowsInSection(0) - 1

    let bottomMessageIndex = NSIndexPath(forRow: bottomRow, inSection: 0)

    guard messages.count > 0
        else { return }

    CATransaction.begin()
    CATransaction.setCompletionBlock({ () -> Void in

        // Now we can scroll to the last row!
        self.tableView.scrollToRowAtIndexPath(bottomMessageIndex, atScrollPosition: .Bottom, animated: true)
    })

    // scroll down by 1 point: this causes the newly added cell to be dequeued and rendered.
    let contentOffset = tableView.contentOffset.y
    let newContentOffset = CGPointMake(0, contentOffset + 1)
    tableView.setContentOffset(newContentOffset, animated: true)

    CATransaction.commit()
}



回答2:


Change UITableViewRowAnimationBottom to UITableViewRowAnimationNone and try




回答3:


Try This!

UITableViewRowAnimation rowAnimation = UITableViewRowAnimationTop;
UITableViewScrollPosition scrollPosition = UITableViewScrollPositionTop;

[self.tableView beginUpdates];
[messages addObject:msg];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:rowAnimation];
[self.tableView endUpdates];

[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:YES];

// Fixes the cell from blinking (because of the transform, when using translucent cells)
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];



回答4:


For Swift 3 and 4

for scroll down to bottom of table View automatically when add new item in the table view just in tableView function add following line its works me

tableView.scrollToRow(at: IndexPath, at: .bottom, animated: true)

for example in my case I have only one section so 0 is use for section and I have list of orderItems so for last index I use orderItems.count - 1

tableView.scrollToRow(at: [0, orderItems.count - 1], at: .bottom, animated: true)




回答5:


I've just found out that on ios 11 this problem no longer exists. So there's no longer a content jump when adding a row to a table view and then scrolling to it with scrollToRow(at:) .

Also, on ios 10 calling scrollToRowAtIndexPath with animated=false fixes the content jump



来源:https://stackoverflow.com/questions/35291117/scrolltorowatindexpathatscrollposition-causing-table-view-to-jump

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!