iOS animation notes

Nov 22, 2015   #iOS  #Objective-C  #Animation  #UIView  #CALayer 

Recently, I had to work on an animated view for an iOS app. I built the view using explicit layer-based animations (CABAsicAnimation and brethren) in a separate app and it worked fine. Then I moved the view into the host application and all hell broke loose. After much fighting with the API, I finally arrived at the techniques needed to ensure that the animations work as intended irrespective of context. This post collects these notes as a list of recommendations to follow.

Use UIView based animations instead of CALayer

UIViews are light weight wrappers on CALayer and have easier APIs to setup animations. So, unless you have super-tight performance requirements, choose to configure your animations using UIView hierarchies. UILabel also renders text better than CATextLayer.

You can animate UIView properties using methods in the categories UIViewAnimationWithBlocks (iOS >= 4.0) and UIViewKeyframeAnimations (iOS >= 7.0).

+ (void)animateWithDuration:(NSTimeInterval)duration 
                      delay:(NSTimeInterval)delay 
                    options:(UIViewAnimationOptions)options 
                 animations:(void (^)(void))animations 
                 completion:(void (^ __nullable)(BOOL finished))completion;

+ (void)animateWithDuration:(NSTimeInterval)duration 
                 animations:(void (^)(void))animations 
                 completion:(void (^ __nullable)(BOOL finished))completion;

+ (void)animateWithDuration:(NSTimeInterval)duration 
                 animations:(void (^)(void))animations;

When you need fine control over your animations, you can dictate the path through a set of key frames using the following -

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration 
                               delay:(NSTimeInterval)delay 
                             options:(UIViewKeyframeAnimationOptions)options 
                          animations:(void (^)(void))animations 
                          completion:(void (^ __nullable)(BOOL finished))completion;

+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime 
                        relativeDuration:(double)frameDuration 
                              animations:(void (^)(void))animations;

The options permit you to use cubic interpolation between the key frames, which makes it possible to implement animations along arc paths without having to work with a path-based explicit layer animation.

Wrap nested layer animations in CATransaction

While the block APIs let you configure UIView parameter animations easily enough, there are some animations you just can’t do using them. For example, animations between paths require CAShapeLayer. For such, you need to configure these animations within the UIView blocks.

The problem is that when you add layer property animations within the UIView animation blocks, they’ll take on a personality of their own and ignore what you say. The only way I could solve this is to wrap these layer animations up in a CATransaction like so -

[CATransaction begin];
[CATransaction setAnimationDuration:2.3];
// Configure layer property animations, including explicit animations.
[CATransaction commit];

This ensures that you can isolate the layer animation properties from the UIView based animation settings.

Note that I’m recommending that you place these layer animations only for those exceptional parts within the UIView block API calls. Apple seems to suggest that we don’t mix these two types of animations, but I’ve not found any other way around my needs, and this seems to work just fine.

Good luck!