Can You Animate a Height Change on a Uitableviewcell When Selected

Swift animate UITableViewCell expanding height constraint

Try without an animation block:

containerHeightConstraint.constant = 410
tableView.beginUpdates()
tableView.endUpdates()

How to animate UITableViewCell height using auto-layout?

The following worked for me:

Preparation:

  1. On viewDidLoad tell the table view to use self-sizing cells:

    tableView.rowHeight = UITableViewAutomaticDimension;
    tableView.estimatedRowHeight = 44; // Some average height of your cells
  2. Add the constraints as you normally would, but add them to the cell's contentView!

Animate height changes:

Say you change a constraint's constant:

myConstraint.constant = newValue;

...or you add/remove constraints.

To animate this change, proceed as follows:

  1. Tell the contentView to animate the change:

    [UIView animateWithDuration: 0.3 animations: ^{ [cell.contentView layoutIfNeeded] }]; // Or self.contentView if you're doing this from your own cell subclass
  2. Then tell the table view to react to the height change with an animation

    [tableView beginUpdates];
    [tableView endUpdates];

The duration of 0.3 on the first step is what seems to be the duration UITableView uses for its animations when calling begin/endUpdates.

Bonus - change height without animation and without reloading the entire table:

If you want to do the same thing as above, but without an animation, then do this instead:

[cell.contentView layoutIfNeeded];
[UIView setAnimationsEnabled: FALSE];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled: TRUE];

Summary:

// Height changing code here:
// ...

if (animate) {
[UIView animateWithDuration: 0.3 animations: ^{ [cell.contentView layoutIfNeeded]; }];
[tableView beginUpdates];
[tableView endUpdates];
}
else {
[cell.contentView layoutIfNeeded];
[UIView setAnimationsEnabled: FALSE];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled: TRUE];
}

You can check out my implementation of a cell that expands when the user selects it here (pure Swift & pure autolayout - truly the way of the future).

UITableViewCell animate height issue in iOS 10

Better Solution:

The issue is when the UITableView changes the height of a cell, most likely from -beginUpdates and -endUpdates. Prior to iOS 10 an animation on the cell would take place for both the size and the origin. Now, in iOS 10 GM, the cell will immediately change to the new height and then will animate to the correct offset.

The solution is pretty simple with constraints. Create a guide constraint which will update it's height and have the other views which need to be constrained to the bottom of the cell, now constrained to this guide.

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UIView *heightGuide = [[UIView alloc] init];
heightGuide.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:heightGuide];
[heightGuide addConstraint:({
self.heightGuideConstraint = [NSLayoutConstraint constraintWithItem:heightGuide attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0.0f];
})];
[self.contentView addConstraint:({
[NSLayoutConstraint constraintWithItem:heightGuide attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f];
})];

UIView *anotherView = [[UIView alloc] init];
anotherView.translatesAutoresizingMaskIntoConstraints = NO;
anotherView.backgroundColor = [UIColor redColor];
[self.contentView addSubview:anotherView];
[anotherView addConstraint:({
[NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:20.0f];
})];
[self.contentView addConstraint:({
[NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f];
})];
[self.contentView addConstraint:({
// This is our constraint that used to be attached to self.contentView
[NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:heightGuide attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
})];
[self.contentView addConstraint:({
[NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeRight multiplier:1.0f constant:0.0f];
})];
}
return self;
}

Then update the guides height when needed.

- (void)setFrame:(CGRect)frame {
[super setFrame:frame];

if (self.window) {
[UIView animateWithDuration:0.3 animations:^{
self.heightGuideConstraint.constant = frame.size.height;
[self.contentView layoutIfNeeded];
}];

} else {
self.heightGuideConstraint.constant = frame.size.height;
}
}

Note that putting the update guide in -setFrame: might not be the best place. As of now I have only built this demo code to create a solution. Once I finish updating my code with the final solution, if I find a better place to put it I will edit.

Original Answer:

With the iOS 10 beta nearing completion, hopefully this issue will be resolved in the next release. There's also an open bug report.

  • https://openradar.appspot.com/27679031
  • https://forums.developer.apple.com/thread/53602

My solution involves dropping to the layer's CAAnimation. This will detect the change in height and automatically animate, just like using a CALayer unlinked to a UIView.

The first step is to adjust what happens when the layer detects a change. This code has to be in the subclass of your view. That view has to be a subview of your tableViewCell.contentView. In the code, we check if the view's layer's actions property has the key of our animation. If not just call super.

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {
return [layer.actions objectForKey:event] ?: [super actionForLayer:layer forKey:event];
}

Next you want to add the animation to the actions property. You might find this code is best applied after the view is on the window and laid out. Applying it beforehand might lead to an animation as the view moves to the window.

- (void)didMoveToWindow {
[super didMoveToWindow];

if (self.window) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
self.layer.actions = @{animation.keyPath:animation};
}
}

And that's it! No need to apply an animation.duration since the table view's -beginUpdates and -endUpdates overrides it. In general if you use this trick as a hassle-free way of applying animations, you will want to add an animation.duration and maybe also an animation.timingFunction.

Animate Height Change for UITableViewCell Without Scrolling

It appears the problem you're encountering is that your table view is scrolled past the bottom so when you update its content it will attempt to fix that.

There are two approaches you could take to prevent scrolling:

  1. Set the table view's content inset to be the height of the initial white space:

    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, verticalGap, 0);
  2. Add an empty footer view with the same height as the vertical gap:

    self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, verticalGap)];

In both cases, you will need to calculate the vertical space you are trying to achieve. You then need to restore the contentInset or tableFooterView to its original state when you are done.

iPhone - Smooth animation for UITableViewCell height change including content update

UITableView has a neat trick to animate changes in cell heights: if you send an empty updates block to the table view, it won't insert or delete any cells, but it will still recalculate cell heights for the entire table. So even though this piece of code looks weird, it should do it:

[tableView beginUpdates];
[tableView endUpdates];

If you want to change the contents of the updated cells, too, you must add one or more calls to reloadRowsAtIndexPaths:withRowAnimation: to the updates block, of course.



Related Topics



Leave a reply



Submit