Core Animation is a data visualization API on both iOS and OS X that allows you to animate the views and other visual elements of your app. Most of the work in drawing each and every frame of the animation is done for you, all you have to do is configure a few animation parameters and tell it to start. At the heart of core animation are layer objects, which we use to manipulate our content.
How did I make this button spin? I didn't drop down to core animation itself and draw each frame. I took advantage of methods that manipulate the underlying core animation layers. The two methods used were introduced in iOS7 which allowed us to use Key Frames in our animation. Both these methods used are class methods on UIView - animateKeyframesWithDuration and addKeyframeWithRelativeStartTime (they go hand in hand).
First, let's establish the key frames. What do I want to animate? How do I want to animate it? Do I want to spin the content, shake it, move it, scale it, transform it? All of these are possible. In this example, I'm going to transform (spin) the image.
How do I do this in code? This button has a .transform property which is a structure that holds an affine transformation matrix (google is your friend). By making our .transform property equal an affine transformation matrix (that differs from the original) then we are transforming our button. But how do we use this information to make our button spin? We set our .transform property to equal CGAffineTransformMakeRotation (CGFloat angle) . CGAffineTransformMakeRotation has a parameter in radians, so lets give it some radians. By passing in Pi ( 3.142 / 180° / π ) we would be transforming our button 180°. This returns an affine transformation matrix (which is what .transform is looking for) constructed from a rotation value you provide. M_PI is a constant in Objective-C which is equal to 3.142 (it's Pi).
//1st Key Frame button.transform = CGAffineTransformMakeRotation(M_PI + (M_PI/4)); //2nd Key Frame button.transform = CGAffineTransformMakeRotation(M_PI - (M_PI/4)); //3rd Key Frame button.transform = CGAffineTransformMakeRotation(0);
The first method we call to establish some rules to how we run through our key frames:
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
The method we use (three times in my example) to establish our key frames and when they kick off. This method is called inside of the animations block in the method above.
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations
Running through the slides:
The duration passed in of 0.6 seconds dictates the entire duration of the animation. It's how long it will take to run through all of the key frames I establish. If I passed in 10 seconds, it would take 10 seconds - it governs the animation. Delay is what you would think it is, whatever thing you have kick off this code, whatever you pass into delay - it will wait that many seconds before running through the key frames. Options allows us control in HOW we run through our key frames. Linear interpolates between the data points, where it creates new data points within a range of a set of data points (this just means that it looks smooth running from one point to the next). If we passed in discrete then instead of interpolating between the points we would jump from one frame to the next (this means that it would look very choppy).
Now we come across our addKeyframeWithRelativeStartTime method. In our first method call we pass in a relateStartTime which LOOKS up to the 0.6 seconds we passed in. Like I stated earlier, the 0.6 seconds governs the entire animation. We can pass in a value from 0-1 here. This just means that, of the 0.6 seconds have to work with - at what point in that 0.6 seconds do we kick off this particular key frame? Here I passed in 0.0 which means of the 0.6 seconds we're working with, KICK this off RIGHT AWAY. The relative duration passed in is 1/3 (which is a value from 0-1). What does that mean? It means that it will run for 0.2 seconds (1/3 of the 0.6 seconds which governs the entire duration). So our first frame kicks off at 0.0 seconds of the 0.6 seconds duration and runs for 0.2 seconds (from it's original state to the 225° rotation). The 2nd frame kicks off at 0.2 seconds (1/3 of 0.6 seconds) and also runs for 0.2 seconds (1/3 of 0.6). The 3rd frame starts at 0.4 seconds (2/3 of 0.6) and also runs for 0.2 seconds (1/3 of 0.2 seconds).
Then we hit our completion block which will run AFTER our animation completes. We can perform additional animations here or do nothing.