Animation Revelation

For the newcomer to iOS development, “animation” may seem like a daunting proposition. That’s how I felt when I wanted to animate a game board 90 degrees in my very first app. How could I possibly do that!? It must be difficult! And so off I went to see how to do it….

Turns out, it was really easy! And it didn’t require much code at all. In fact, the iOS SDK provides a lot of support for adding animation to your UI. You can do many thing with just a little code and achieve some stunning effects.

Project X has a few places where animation is used. One place, as I wrote about last time, is to rotate the game board and game pieces. These animations are “simple” in that they rotate objects point A to point B, if you will. The app on which Project X is based included another rotation animation for each game piece if the user tried to choose one that was not in the set of allowed next game pieces.

For Project X I wanted a different effect to indicate “sorry, you can’t pick that one.” I immediately thought of the animation you can see if you try to login to your Mac using the wrong password. The input field shakes left to right a couple times, then settles back to it’s original position. Sounds simple enough…

I first tried to use the animation tricks I already knew and was familiar with. Namely things like:

[UIView animateWithDuration:...
        delay:...
        options:...
        animations:^{ ... }
        completion:^(BOOL f) { ... }
];

The problem with this approach is that I would have had to nest multiple calls to -animateWithDuration: in each call’s completion block. Messy, at best.

After some thought I remembered there was something called Key Frame Animation and set out to refresh my memory.

Key Frame Animation is pretty powerful; you can do a lot of really cool things with it, from moving objects on the screen to scaling and resizing, and much more. For my desired effect, Key Frame Animation proved to be just the thing, and the solution was elegant.

You can download a sample project from github, but here is the one method that makes it all work:

-(IBAction)buttonPress:(id)sender
{
    NSString *keyPath = @"anchorPoint.x";    
    CAKeyframeAnimation *kfa = [CAKeyframeAnimation animationWithKeyPath:keyPath];
    [kfa setValues:[NSArray arrayWithObjects:
                    [NSNumber numberWithFloat:-.05],
                    [NSNumber numberWithFloat:.1],
                    [NSNumber numberWithFloat:-.1],
                    [NSNumber numberWithFloat:.1],
                    [NSNumber numberWithFloat:-.05],
                    nil]];    
    [kfa setAdditive:YES];
    [kfa setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    [kfa setDuration:.35];
    [button.layer addAnimation:kfa forKey:nil];

}

There are a few things to take note of here. First, animations are applied to the object’s layer, so you’re dealing with layer properties. For this effect, we’re animating a button’s anchorPoint.x property. That is, we’re specify the changes to be made to that property, on a per-frame basis. (Think of a frame as an incremental animation of your object.)

The setValues: method receives an array of values to apply to the button’s anchorPoint.x property. These values are specified each in the range from -1.0 to 1.0. The anchorPoint.x range is from 0 to 1; it’s value represents a relative position within the object. The default value for anchorPoint is {0.5, 0.5}, or the middle of the object.

The values you specify can be applied cumulatively or additively. The former is good if you want to move an object across the screen — from point A to point B. Additive, on the other hand, works really well for our purposes because we want to move the object relative to its anchor point. We want to move the button left by -.05, then right by .1 (which is +.05 in total), then back by .1 (now we’re back at -.05), etc. So the animation here goes left, right, left, right, and back to center (back to where it started).

I chose the Ease In/Easy Out timing function so that the button sort of winds up, shakes, then winds down. There are several timing functions to choose from, and I thought this one produced a nice visual effect.

Finally, we add the Key Frame Animation object to the button’s layer (button is a class variable).

That’s it! It took me about an hour to work this out, only because I had not ever done anything with Key Frame Animation before. And, not surprisingly, the solution is short and sweet.

Posted in: iDevBlogADay

Leave a Reply

Your email address will not be published. Required fields are marked *

Prove you are human: *

Read previous post:
OS X Lion + Oracle + ruby-oci8 Gem Solution

In my day job, we have need to interface with an Oracle database, from ruby. There's a gem for that:...

Close