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
UIView
s 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!