Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView animation glitch when deleting and inserting cells with varying heights

Tags:

uitableview

I am having a problem with the animation that UITableView provides when deleting and inserting a cell at the same time. I have a list of cells lets call them questions. When one question is tapped it should add a cell beneath itself to display the answer to that question. If another answer is already being displayed that answer should be removed from the table. The issue arises when the cell being inserted is very tall. If it is so tall that it's eventual bounds encroach into the space that the cell to be deleted takes up then during the animation we see the through the answer cell that we are deleting to see the cell that is being added

(see link to video of problem)

the is what my code looks like to move around the cells in the table view

[tableView beginUpdates];

if (deleteIndex) {
    [tableView deleteRowsAtIndexPaths:@[deleteIndex] withRowAnimation:UITableViewRowAnimationTop];
}
if (addIndex) {
[tableView insertRowsAtIndexPaths:@[addIndex] withRowAnimation:UITableViewRowAnimationTop];
}

[tableView endUpdates];

I have tried

[tableView beginUpdates];

if (deleteIndex) {
    [tableView deleteRowsAtIndexPaths:@[deleteIndex] withRowAnimation:UITableViewRowAnimationTop];
}
[tableView endUpdates];

//do stuff to update data source

[tableView beginUpdates];

if (addIndex) {
[tableView insertRowsAtIndexPaths:@[addIndex] withRowAnimation:UITableViewRowAnimationTop];
}

[tableView endUpdates];

But because there's no callback confirming that the table view did complete the first set update before starting the second block pretty much the same problem occurs. I know I could use perform selector with delay, but this seems like a bandaid.

Second I tried to encompass the animation in a block like this

[UIView animateWithDuration:0.0 animations:^{
    [tableView beginUpdates];

    if (deleteIndex) {
        [tableView deleteRowsAtIndexPaths:@[deleteIndex] withRowAnimation:UITableViewRowAnimationTop];
    }
    [tableView endUpdates];

    //do stuff to update data source

} completion:^(BOOL finished) {

    [tableView beginUpdates];

    if (addIndex) {
        [tableView insertRowsAtIndexPaths:@[addIndex] withRowAnimation:UITableViewRowAnimationTop];
    }

    [tableView endUpdates];
}];

Again, because the completion block is fired after we call endUpdates not after the updates actually complete this does not resolve the use.

I also went into storyboard to be sure that clip subviews to bounds is selected for the cells. Again, this does not resolve the issue because we are not seeing a subview of the cell expand beyond it's expected height.

Looking closer at the animation by slowing it down it looks like apple inserts the cell to be added under the cells that won't be changed in the table and then moves the cells that will remain into their new positions. As a result the cell that was deleted becomes a transparent window where we see what they are actually doing under the hood.

like image 538
Nat Osten Avatar asked Jan 24 '13 01:01

Nat Osten


1 Answers

The right approach would be to first remove the old answer and then after the animation is over add the new answer. There is a UITableViewDelegate method that is triggered after the cell animation is complete.

tableView:didEndDisplayingCell:forRowAtIndexPath:

Inserting the new answer row within this delegate method will result in the correct animation.

There are a few details to keep in mind- You'll need some logic to ensure that the correct cell height is returned and that the correct number of expected rows in the section is returned. Those data source methods are called after we remove our old answer in and again when we add the new one when we call

endUpdates

on the table view

Using anything other than UITableViewRowAnimationTop results in some strange animation behavior. This is because the content view of the cell is not what is being animated.

like image 89
Nat Osten Avatar answered Jan 04 '23 14:01

Nat Osten