3

I want to perform two simultaneous movement animations. First animation on firstView starts immediately. Second animation on secondView, starts after a slight delay while the first animation is still running. secondView constraint is related to firstView. The code works perfectly well on iOS 8.

firstView and secondView are subviews of view

view  
    |--- firstView  
    |--- secondView    

Code:

UIView *firstView = ...;
UIView *secondView = ...;    

[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:firstView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
    [self.view addConstraint:constraint];
    [self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];

[UIView animateWithDuration:0.5 delay:0.15 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:secondView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:firstView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
    [self.view addConstraint:constraint];
    [self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];

On iOS 7, once the second layoutIfNeeded is called, the first animations stops and only the seconds animation animates.

Any suggestions?

tonymontana
  • 5,728
  • 4
  • 34
  • 53
  • Try UIViewAnimationOptionLayoutSubviews instead of calling layoutIfNeeded directly. Although that does basically the same, maybe it helps. – Micky Feb 17 '15 at 09:26
  • @Kim Tried that already. Didn't help. – tonymontana Feb 17 '15 at 09:39
  • If you don't find a solution with UIView animations, you could try UIKit dynamics instead. There's a good tutorial here: http://www.raywenderlich.com/50197/uikit-dynamics-tutorial – Micky Feb 17 '15 at 10:04

2 Answers2

2

Answering my own question.

I ended up doing the animation in a 2-step (3-step, depends how you count) solution. First add the constraints without calling layoutIfNeeded. Then update the firtView and secondView frames. Lastly call layoutIfNeeded in the completion block of the last animation.

Code:

UIView *firstView = ...;
UIView *secondView = ...;    

/* first step */
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:firstView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[self.view addConstraint:constraint1];
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:secondView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:firstView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[self.view addConstraint:constraint2];

/* second step */
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    CGRect firstViewFrame = CGRectMake(...); // set frame according to constaint1
    firstView.frame = firstViewFrame;
}];

[UIView animateWithDuration:0.5 delay:0.15 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    CGRect secondViewFrame = CGRectMake(...); // set frame according to constaint2
    secondView.frame = secondViewFrame;
} completion:^(BOOL finished) {
    /* second and a half step */
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];
tonymontana
  • 5,728
  • 4
  • 34
  • 53
0

Two important notes:

  1. You need to call layoutIfNeeded within the animation block. Apple actually recommends you call it once before the animation block to ensure that all pending layout operations have been completed.
  2. You need to call it specifically on the parent view (e.g. self.view), not the child view that has the constraints attached to it. Doing so will update all constrained views, including animating other views that might be constrained to the view that you changed the constraint of (e.g. View B is attached to the bottom of View A and you just changed View A's top offset and you want View B to animate with it).

You must do it like this:

[self.view layoutIfNeeded];

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:firstView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[self.view addConstraint:constraint];

[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    [self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
Akshay Sunderwani
  • 12,428
  • 8
  • 29
  • 52