January 21, 2012

CAKeyframeAnimation — Make it bounce

Update (Aug 2012): I’ve made a class called SKBounceAnimation that handles almost all the heavy lifting for you. Check out the blog post and view it on Github.

UIScrollViews, which are the main elements that move in iOS, have momentum, friction, and bouncing built into them. To design an animation with just momentum and friction, we can use the simple, built-in ease-in-ease-out timing functions. To include a bounce, however, we need to switch to a more complex technique.

We could simulate this by using multiple animations. In the completion block of the “overshoot” animation, you could create another animation that bounces back the object back and you could do this a few times until you have all the bounces you want. A hack, essentially.

Theory

What if we could describe all of the motion with one equation, and tell the system to just animate just once along that path?

Fortunately for us, we can. In my previous post, I describe a mass-spring-damper system, which simulates the real-world motion of an object. Solving our mass-spring-damper for a system with an initial displacement and simplifying, we get this result:

This solution makes us happy, because the e exponent portion approaches zero as t gets bigger, and the cosine portion just behaves in a cyclical manner. So over time, this will cyclically approach zero, which is perfect. We can graph it and see that it behaves the way we want it to.

Creating a KeyframeAnimation

To set up the CAKeyframeAnimation, we create a Keyframe Animation using a key path, which just tells the animation what properties to animate. You can find a full list of animatable properties and their key paths here on Apple’s site.

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.duration = 0.2;

Next, we create an array to store the values of our animation. iOS will interpolate for us, but we create enough points so that the interpolation can just be linear. We use our damped oscillation function, and fill an NSMutableArray with values describing the motion. The values I chose, -0.3 and 0.35, were found by just experimenting, and finding values that had the number of bounces and duration that I wanted. You can calculate precise values by looking at the previous post and selecting values for m, c, and k.

int steps = 100;
NSMutableArray *values = [NSMutableArray arrayWithCapacity:steps];
double value = 0;
float e = 2.71;
for (int t = 0; t < steps; t++) {
	value = 320 * pow(e, -0.055*t) * cos(0.08*t) + 160;
	[values addObject:[NSNumber numberWithFloat:value]];
}
animation.values = values;

We calculate values for our animation for each step, and add these values to our values array. I’ve added a little math to make sure that the function provides the right value to change position.x (the position of a layer corresponds to the center of a view). CAKeyframeAnimation also lets us set timing functions for each step and the specific times that each step should occur at, but they’re unnecessary for this.

[upperView.layer setValue:[NSNumber numberWithInt:160] forKeyPath:animation.keyPath];
[self.upperView.layer addAnimation:animation forKey:nil];

Finally, we set the keypath property for the layer to the final value. We attach it to the layer of the view we want to animate. If we don’t set the final value of the animation before attaching the animation, the layer will snap back to its original state after the animation is over, and it won’t work.

Implicit vs. explicit animations

iOS distinguishes between implicit and explicit animations. An implicit animation is the more common type, where we use the UIView class function +(void)animateWithDuration:. In this case, we’re using a explicit animation, where we create a CAAnimation object and attach it to the layer that we want to animate. This gives us much finer-grained control, but requires us to tell the layer what the final value for the keypath is.

Patents

When working with physics simulation in touch devices, you need to be wary of the patents that Apple and other companies have on this technology. Apple’s patents, as far as I know, only protect algorithms that use track touches to determine the user’s finger’s speed. Since that’s not involved here, we are safe to use it. If there is another patent that is related here that I haven’t mentioned, please send me a tweet or an email, and let me know.

SKBounceAnimation

If you’re just making a simple bounce animation, I highly recommend you use SKBounceAnimation. It makes it very easy to do all these things and takes the math out of it. All you do is set the number of bounces, and it takes care of the rest for you.