iOS Animation With Layers
The fourth generation of the iPhone, the iPod Touch as well as the third generation of the iPad offer increasingly powerful graphics hardware. This is useful not only for games, but also for improved user interfaces, and a better user experience.
This articles covers an important aspect of the fundamental graphics API of iOS – native support for animations. For an introduction to the basic Quartz API please consult the online documentation. It is useful to be familiar with the basic concepts of Quartz before proceeding, but anybody with thorough knowledge of that manual will most likely not need to read this article.
The easiest way to add animations to iOS apps is to use the block-based animations that all instances of
UIView and their sub-classes support:
This command triggers an animation with a duration of ten seconds and smoothly varies the location and the size of a view
myLabel. For the starting and ending phase of the animation it uses the Smoothstep function by default.
Note that this animation uses a class method of
UIView, even though the animation actually modifies properties in specific instances of subclasses of
A drawback of this approach is that there is no callback (delegate) feature that could get called and refer to the current state of the animation. Furthermore, the
animateWithDuration:… methods all return immediately, and the view’s properties adopt their final destination values immediately – even while the animation is still in progress. This results in a mismatch of the view’s current properties on the one hand and the actual state on screen. We will see below how to deal with this situation and resolve this discrepancy below.
Animating Curves and Paths
Basic Animation Revisited
For the animations discussed below we need to rewrite the above call as follows:
This approach looks a little more “old-school”, but it is necessary for animating other things than the elementary view properties discussed above. It is, in fact, the fundamental native interface to layer animation.
A path is another fundamental concept of Quartz 2D. It is a collection of individual components like lines and arcs that can be drawn and animated. The following code constructs an elementary path with three lines that make up a triangle:
One way to use a path is by setting the
path property of a
CAShapeLayer. All the drawing-related information likes shadows, fills and colors are configured on the
CAShapeLayer. Thus, the path could be drawn in a view as follows:
It is important to manually release any path object that has been created with
CGPathCreateMutable() as this function is a C-style interface and thus not handled by automatic reference counting (ARC). Outside a subclass of
UIView the reference to
self must be replaced by an appropriate different instance.
The basic animations of
UIView cannot be used to animate path objects introduced above. The more fundamental animations of class
CABasicAnimation, however, can. The following code animates the
path property of a
This code fragment needs two objects of class
CGMutablePathRef. One is the starting path, in this case
aPath shown above. The other one is the shape that is supposed to be shown at the end, denoted by
anotherPath. With this approach it is possible to animate arbitrary shapes and objects on screen without needing to compute intermediate points and locations. It is entirely sufficient to only provide the starting and ending shapes and Quartz figures out how to do the rest!
It is also possible to configure animations to repeat and/or reverse by setting appropriate options on the instance of
Fills, Shadows, 3D — Oh My!
The above code demonstrates how not only elementary shapes can be drawn and animated, but all relevant properties set in the embedding layer will be used in the transitions. The
CAShapeLayer object introduced above had a custom line width, line stroke color, filling color and shadow. Setting these properties automatically drew the shadow and filling on the path during the entire animation!
Quartz has several more subclasses of
CALayer, most of which are fully animatable and thus can be used for stunning visual and animation effects:
- Draws colored gradient fills in the shape of its layer. Note that the colors themselves are animatable properties. See the developer documentation and note that this class makes use of the GPUs on modern iOS devices!
CALayerobject has a
transformproperty which not only accepts a 2-dimensional transformation matrix, but also accepts a 3-dimensional
CATransform3Dstruct. Needless to say that either one is animatable! This approach is kind of inconvenient, because it will flatten the sub-layers. One way to avoid this is to use a
CATransformLayerthat does not flatten its sub-layers and allows even other three-dimensional layers as sub-layers, see the developer documentation.
CAReplicatorLayercovers a surface with copies of one original layer. The really powerful feature of this approach is that it is possible to also let animations applied to the original layer be automagically deployed to the copies, resulting in extremely complex animation patterns with just a few lines of code. For macOS Apple has a demo, for iOS there is only the developer documentation.
- To access the current state of an ongoing animation use the
presentationLayerproperty to investigate the current state of an animation. Unlike the animatable properties – which are set to their final destination values as soon as the animation call returns – the
presentationLayergrants access to the current state of the animation and thus the animatable properties. Note that this may be a little more complicated, though, because the
presentationLayeris an instance of
CALayer, not of the original
UIViewwhose properties are animated.
Basic and Advanced Animation of Labels
UILabel is a sub-classes of
UIView all the animatable properties discussed above are applicable. The code fragment shown above in the basic animations section is fully applicable here. However, there are a couple of additional things we can configure on labels while an animation is in progress. Even things that are not related to animatable properties! In the following I demonstrate a few of those.
Modifying the View Hierarchy and Content
A particularly powerful method to influence the presentation of labels is to use
NSTimers to control the content of the label. This even works for the entire view hierarchy, as we can remove the label and reinsert it even as the animation progresses!
First, we set up a timer and demonstrate removing and re-adding the label:
First, a timer is set up that calls the method
callMe: every 0.1 seconds. Three seconds into the animation of the label, it is being removed from the view hierarchy and five seconds after the animation began it is being added again. Thus, it is possible to manipulate the view hierarchy during an ongoing animation.
Next, we go a step further and modify the content of the label text while it is being moved around:
Now the label will contain a counter which updates every 0.1 seconds while it is being moved around.
Full Redraw of a Scene
Similar to the OpenGL callbacks it is possible to manually perform full screen redraws in case the animation procedures listed above are still insufficient. The basic mechanism is to use the class
CADisplayLink, see the developer documentation.
CADisplayLink is tied directly to the refresh rate of the display and can thus perform a full screen redraw while having access to the actual animation time. In this way it is the most powerful and generic way to circumvent the limitations of
UIView animations discussed above.
A specific example of how to use
CADisplayLink is given in this stackoverflow.com question, see the accepted answer.
The techniques shown in this post can be combined and run asynchronously. In this way it is possible to build quite complex visualizations from basic building blocks.
A sample project with several of the techniques discussed above is available on github.com.