Search code examples
iosios7ios8autolayoutios-autolayout

iOS - Performing several simultaneous animations with autolayout


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?


Solution

  • 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];
    }];