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:
On
viewDidLoad
tell the table view to use self-sizing cells:tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 44; // Some average height of your cellsAdd 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:
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
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:
Set the table view's content inset to be the height of the initial white space:
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, verticalGap, 0);
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
Uidevice Uniqueidentifier Deprecated - What to Do Now
Getting the Difference Between Two Dates (Months/Days/Hours/Minutes/Seconds) in Swift
Instantiate and Present a Viewcontroller in Swift
When Should I Compare an Optional Value to Nil
How to Create a Uicolor from a Hex String
How to Apply Gradient to Background View of iOS Swift App
Importing Commoncrypto in a Swift Framework
Autoresizing Masks Programmatically VS Interface Builder/Xib/Nib
Prevent Screen Capture in an iOS App
How to Hide Keyboard When Using Swiftui
How to Access a Specific Field from Cloud Firestore Firebase in Swift
React Native App Works on Debug Mode, But Not Works Release Mode on Ios
How to Load an Http Url With App Transport Security Enabled in iOS 9
Opening the Settings App from Another App
Ios 9 Not Opening Instagram App With Url Scheme
Uiimage(Contentsoffile:) Returning Nil Despite File Existing in Caches Directory