Category Archives: iOS

How to synchronize UIView and CALayer animations

Core Animation layers come very handy for rendering simple secondary views. One thing I really love about them is the implicit animations – they often add that nice-to-have touch to something I may not have bothered with. However, things can get not so nice when trying to pair them up with regular UIView transitions. I discovered this recently when trying to re-size the frame of a UIView and the corresponding frame of a CAGradientLayer behind it. The default result simply did not look good. By the nature of being implicit, layers follow their own inner instructor and ignore UIKit commands. They are also only about 0.25 seconds in duration, which works for smaller changes but is often not enough for transforming bigger elements. For that reason, simply matching the same duration on the UIView was not the answer. Well, not the full answer..

The first step in overriding implicit animations was to create a CABasicAnimation and wrap it inside a CATransaction block. Then the matching trick comes in and you need to make sure that the duration and the ease curve are equivalent in both blocks. So in this case I used kCAMediaTimingFunctionEaseOut in CATransaction with the corresponding UIViewAnimationOptionCurveEaseOut in UIView.

And at last, a perfect animation unison was found.

float duration = 0.5;////////////////////////////
// CALayer part
///////////////////////////[CATransaction begin];
[CATransaction setAnimationDuration:duration];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];

CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];

[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"myAnimationKey"];
[CATransaction commit];

///////////////////////////
// UIView part
//////////////////////////
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         myView.bounds = myNewFrame;    
                     }

                     completion:^(BOOL finished) {                 
                     }];