Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animation looks awkward when deleting a cell whose height is greater than other cells

I have a plain UITableView with 4 rows, each with a height of 50. When I press the fourth row, I insert a fifth row with a height of 80 using UITableViewRowAnimationTop. So far, so good.

I want to delete the fifth row when the fourth row is pressed again. But when I delete the row using UITableViewRowAnimationTop (or any other animation style for that matter), the animation looks very awkward--the animation begins but the cell disappears abruptly before the animation is complete. (This is only apparent when you're using cells that have a background color. You can see the bottom half of the cell suddenly disappear rather than disappearing under the cell above it.)

The code is something like this:

[self.tableview beginupdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
[self.tableview endupdates];

The row heights are provided by the tableView:heightForRowAtIndexPath: method.

This problem does not occur when all rows have the same height, or when the row being deleted is shorter than the other rows.

I think one workaround is changing the height of the fifth cell to 50 before deleting it, but I'd rather not do that.Please help

like image 382
P. Wright Avatar asked Apr 17 '12 13:04

P. Wright


2 Answers

I just ran into this problem this morning. Basically, I ended up handling the animation myself, then deleting the cell once the animation completed. Here's some code to show how I did that:

@property (nonatomic, strong) NSArray *tableData;
@property (nonatomic, strong) NSMutableDictionary *deletedItems;
...

- (void)deleteItemForCell:(UITableViewCell *)cell
{
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    NSObject *item = self.tableData[indexPath.row];

    NSNumber *rowKey = @(indexPath.row);

    __weak id weakSelf = self;
    [CATransaction setCompletionBlock:^{
        @synchronized(weakSelf) {
            [self.deletedItems removeObjectForKey:rowKey];
            [self.tableData removeObject:item];
            [self.tableView deleteRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationNone];
        }
    }];
    self.deletedItems[rowKey] = item;

    [self.tableView beginUpdates];
    [self.tableView endUpdates];

    [CATransaction commit];
}

- (CGFloat)tableView:(UITableView *)tableView
    heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.deletedItem[@(indexPath.row)]) {
        return 0;
    }
    return CELL_HEIGHT;
}

The code tracks deleted items in the data structure and informs the table to update. Any items in the deleted data structure will get a height of 0, which will result in a "collapse" animation.

The code utilizes a CATransaction to know when the animation has completed. Once it's completed the item is deleted from the tableData data structure and deleted from the table, without any animation.

NOTE:

  • I'm not sure if the synchronized is required, but seemed smart to have, since we're dealing with asynchronously deleting items.
  • I tried using the indexPath object as the key for the dictionary, but it didn't seem to work. Used the row instead.

Hope this helps.

like image 98
Fostah Avatar answered Oct 18 '22 22:10

Fostah


Using a UITableViewRowAnimationMiddle made it slightly less terrible for me.

Similarly, I noticed that with UITableViewRowAnimationTop, the height beyond the tableviews default cell height appears/disappears without animation. So if your tableview row height is 44, but you animate in a cell with height of 74, it will immediately show 30, and then animate in the remaining 44.

With UITableViewRowAnimationMiddle the cell content animates in nicely, but the cell border is glitchy for the duration of the animation - originally appearing at 30, then animating the remaining 44.

like image 38
mkirk Avatar answered Oct 18 '22 22:10

mkirk