I was having a similar problem and this thread was a great help to get past it.
The answer from erurainon put me on the right track, but I'd like to propose a slightly different answer. The suggested code from erurainon did not work for me, as I still had a jump instead of an animated transition. The link provided by cnotethegr8 gave me the working answer:
Auto Layout Guide
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html (all the way to the bottom of the page).
A few differences from the answer by erurainon:
- Call layoutIfNeeded on the container view before the call to an animation method (and instead of setNeedsUpdateConstraints on myView).
- Set the new constraint in the animations block.
- Call layoutIfNeeded on the container view in the animations method (after setting the constraint), instead of on myView.
This will adhere to the pattern suggested by Apple in the link above.
An example
I wanted to animate a particular view, closing or expanding it at the click of a button. Since I'm using autolayout and didn't want to hard code any dimensions (in my case height) in the code, I decided to capture the height in viewDidLayoutSubviews. You need to use this method and not viewWillAppear when using autolayout. Since viewDidLayoutSubviews may be called many times, I used a BOOL to let me know about the first execution for my initialization.
// Code snippets
@property (weak, nonatomic) IBOutlet UIView *topView; // Container for minimalView
@property (weak, nonatomic) IBOutlet UIView *minimalView; // View to animate
@property (nonatomic) CGFloat minimalViewFullHeight; // Original height of minimalView
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *minimalViewHeightConstraint;
@property (nonatomic) BOOL executedViewDidLayoutSubviews;
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// First execution of viewDidLayoutSubviews?
if(!self.executedViewDidLayoutSubviews){
self.executedViewDidLayoutSubviews = YES;
// Record some original dimensions
self.minimalViewFullHeight = self.minimalView.bounds.size.height;
// Setup our initial view configuration & let system know that
// constraints need to be updated.
self.minimalViewHeightConstraint.constant = 0.0;
[self.minimalView setNeedsUpdateConstraints];
[self.topView layoutIfNeeded];
}
}
Resize full action snippet
// An action to close our minimal view and show our normal (full) view
- (IBAction)resizeFullAction:(UIButton *)sender {
[self.topView layoutIfNeeded];
[UIView transitionWithView:self.minimalView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.minimalViewHeightConstraint.constant = 0.0;
// Following call to setNeedsUpdateConstraints may not be necessary
[self.minimalView setNeedsUpdateConstraints];
[self.topView layoutIfNeeded];
} completion:^(BOOL finished) {
;
}];
// Other code to show full view
// ...
}
Resize small action snippet
// An action to open our minimal view and hide our normal (full) view
- (IBAction)resizeSmallAction:(UIButton *)sender {
[self.topView layoutIfNeeded];
[UIView transitionWithView:self.minimalView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.minimalViewHeightConstraint.constant = self.minimalViewFullHeight;
[self.minimalView setNeedsUpdateConstraints];
[self.topView layoutIfNeeded];
} completion:^(BOOL finished) {
;
}];
// Other code to hide full view
// ...
}
You can use animateWithDuration instead of transitionWithView if you wish.
Hope this helps.