Creating an iPad flip-clock with Core Animation

flipclock_oneAs part of the sliding puzzle game I’m developing for the iPhone and iPad (well, I can’t survive on the profits from BattleFingers forever), I looked for a way to represent a numeric score and time display in an interesting way. One of the nicer visual effects you could use for this is the “flip-card clock” style, where each number consists of a top and bottom part, and the top part flips down to reveal the next number. It’s been used in a few other places including the home screen in the HTC Diamond device, and its physical, realistic style fits well with the iPad, so I set about creating a version for the iPhone and iPad using the built-in Core Animation library. Read on for more details.

I started by thinking about the motions that would be required for the animation. We can break it down as three elements, the top, the bottom and the flipping part. Each of these parts can be represented as a Core Animation Layer (a CALayer). We create them using the class method on CALayer, there’s no additional retain here, as the _toplayer property is retained.

_toplayer = [CALayer layer];

Splitting images

Now we’ve got the layers, we need something to but into them. I didn’t want to create lots of pre-processed images for the top and bottom sections of each number, so instead I wrote a routine to split the images in code, drawing each half into an image context and grabbing a UIImage from it. This is then stored in an array for later use. We can get any of the elements from the array and set it as the content of the layer as required.

// The size of each part is half the height of the whole image
CGSize size = CGSizeMake([img size].width, [img size].height/2);
 
// Create image-based graphics context for top half
UIGraphicsBeginImageContext(size);
 
// Draw into context, bottom half is cropped off
[img drawAtPoint:CGPointMake(0.0,0.0)];
// Grab the current contents of the context as a UIImage 
// and add it to our array
UIImage *top = [UIGraphicsGetImageFromCurrentImageContext() retain];			
[_imagesTop addObject:(id)top];
 
// Now draw the image starting half way down, to get the bottom half
[img drawAtPoint:CGPointMake(0.0,-[img size].height/2)];
// And store that image in the array too
UIImage *bottom = [UIGraphicsGetImageFromCurrentImageContext() retain];			
[_imagesBottom addObject:(id)bottom];
 
UIGraphicsEndImageContext();

Now our _imagesTop and _imagesBottom arrays contains all the images we need, let’s get ‘em moving.

Applying animation

The axes and anchor point of a CALayer

The axes and anchor point of a CALayer

The animation we need to apply to the ‘flipping’ part is a rotation around the bottom of the layer, at the join between the two halves. The default location of the anchor point is the middle of the layer, which would mean the piece would rotate around the middle, but we can easily change it to instead be at the middle in the bottom (for our purposes it could be anywhere on the bottom, as we’re only interested in x-axis rotation):

_fliplayer.anchorPoint = CGPointMake(0.5, 1.0);

I use the CATransform3DMakeRotation function to create a transform around the X axis using the vector [1,0,0]. For the first part of the motion, we go from 0 radians to pi/2 radians (0 to 90 degrees), using an explicit CABasicAnimation object:

CABasicAnimation *topAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
topAnim.duration=0.25;
topAnim.repeatCount=1;
topAnim.fromValue= [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0, 1, 0, 0)];
float f = -M_PI/2;
topAnim.toValue=[NSValue valueWithCATransform3D:CATransform3DMakeRotation(f, 1, 0, 0)];
topAnim.delegate = self;
topAnim.removedOnCompletion = FALSE;
[_fliplayer addAnimation:topAnim forKey:[NSString stringWithUTF8String:k_TopAnimKey]];

Drawing layer content

Unfortunately a CALayer doesn’t have a “back”, so creating the flip layer is not as easy as it could be. We need to add a step in the process to change the image displayed by the flip layer half way through its fall. We can do this in a variety of ways, I’ve chosen to set a delegate object to perform the drawing of the layer contents:

[_fliplayer setDelegate:_layerHelper];

This means our delegate object (_layerHelper) has its drawLayer method called whenever the layer needs to display its contents. Be wary though, this may not be as often as you think! Core graphics aggressively caches the contents of the layer, and will only call your delegate when absolutely necessary. You can force it to happen by calling setNeedsDisplay, which invalidates the contents of the layer:

[_fliplayer setNeedsDisplay];

I encapsulated this in the helper object itself, by providing a custom implementation of the isTop property setter that calls setNeedsDisplay. At least that way callers don’t have to be aware of this requirement.

The drawLayer method itself simply draws one of two images into the context, depending on whether it’s in ‘top’ mode. A slight further complication is that when drawing the bottom part, the image needs to be inverted to display upside down. This is achieved by applying a translation and scale to the context before drawing the image:

CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0f, r.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
 
CGContextDrawImage(context, r, [_imgTop CGImage]);
 
CGContextRestoreGState(context);

Avoiding implicit animation

I’ve tried to avoid using implicit animation here, instead using explicit construction of CABasicAnimation objects, because we need some control of when things happen, and the order in which they happen. Normally, it would be easier to just set some property of the view and let the OS manage animating it. Many properties have default transition that are applied whenever they’re changed, so it takes some additional effort to make them change immediately.

In our example we change the contents of the top half and show the flip layer, but we need that to happen immediately so we disable actions within an explicit transaction that we then commit. This gives us the control we need, and avoids the default behaviour of the transition occurring when the message loop next runs.

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
				forKey:kCATransactionDisableActions];
_toplayer.contents = (id)[[_imagesTop objectAtIndex:[self getNextIndex]] CGImage];
_fliplayer.hidden = NO;
[CATransaction commit];

Getting some perspective

Although the images I’ve used to illustrate this post are isometric, what we really want is a front-on perspective view, so that the falling piece seems to stick out. For this we have to set a sublayerTransform on the view’s inherent layer, by setting an individual element on a transform:

CATransform3D aTransform = CATransform3DIdentity;
float zDistance = 1000;
aTransform.m34 = 1.0 / -zDistance;	
[self layer].sublayerTransform = aTransform;

This gives us a much more realistic look.

Putting it together

Wow, so now we’ve got some appropriately sliced images, some layers and some animations. Let’s put them together to get the final effect.

The animation states (last is same as first)

The animation states (last is same as first)


I’ve used a very simple state machine that moves through 3 states; TopToMiddle, MiddleToBottom, ResetForNext. We can use the animationDidStop delegate as the state transition point. We don’t have to bother determining exactly which animation finished, because we do the same thing (change state) regardless.

- (void)animationDidStop:(CAAnimation *)oldAnimation finished:(BOOL)flag
{
	[self changeState];
}
  • TopToMiddle: show flip layer, and start rotation through 90 degrees
  • MiddleToBottom: change image on flip layer, start rotation through further 90 degrees
  • ResetForNext: change displayed top and bottom layer contents and hide flip layer

Let’s see how it looks in action:

This entry was posted in Graphics, iPhone, Mac and tagged , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
  • Mike

    This is a great concept! Thanks fr the info — would you be able to provide code so we can see the whole thing in action?

  • ian

    Thanks for the comment. I’ll see what I can do with regard to the code.

  • Mike

    Thanks!

  • http://jzlabs.com jason

    Hi Ian, I’d be interested in seeing the code in action as well….thanks!

  • Mike

    I’ve been fumbling around with this for a while — lol — new to CoreAnimation, any idea when you could post code so I can see where I’m going wrong?

    Thanks!

  • http://www.nobelundhenning.de Julian Krenge

    This is a great concept you’re presenting there. I also thought that this flip-clock style would look awesome in my app. Initially I wanted to created the animations manually but your concept seems far more convincing since it lowers overhead a lot.

    I have a question though. You mentioned, that you’re using a state machine with three states. But this somehow implies, that you first finish the whole animation and afterwards display the correct number. But right after the start of the animation, which means about half through the state TopToMiddle the user is able to see the non-updated top-half of the digit. Is this correct? For instance, when you switch from 1 to 2, you acutally flip from 1 to 1 and then change the number to 2? Anyhow, I cannot observe this behaviour in the video. Is this just because my eyes are not fast enough to track the, lets call it, inaccuracy?

    Another question regards the use of the animations: do you have any information on the energy consumption of a app which uses Core Animation rather than animated images?

    But again, thanks a lot for sharing your thoughts, I’d really helped me a lot. Also, if you wouldn’t mind sharing the code, I would appreciate it a lot as well. Although I’m not sure whether this will help more than confuse me. ;-)

    Like you blog, keep up the good work! :-)

  • ian

    Thanks for the comments Julian.

    The answer to your question is that first state has knowledge of the next number to be displayed; so before flipping down the moving piece the ‘static’ top piece can display it. That’s what’s in the first of the 4 images in the graphic. There shouldn’t be any ‘fudging’ required!

    This approach may use marginally more CPU cycles than a simple animation, but I don’t have any figures to hand. Some of the cost will be upfront in splitting the images. Bear in mind that it will require less storage and therefore your binary will be smaller and possibly faster to load. There are trade-offs.

  • http://www.nobelundhenning.de Julian Krenge

    Thanks for your response, Ian.

    I understand your comment on the animation, this obvious solution somehow didn’t occur to me. Also your arguments on the 3D-animation vs. images are very valid. As you stated in your initial post, it’s also a lot work to create the animations manually.

    In the meantime I finished the animation as well, only with a slight difference to your approach. I was to lazy changing the flip-layers content in the middle, so I use one layer flipping up and one layer flipping down. Maybe I will refactor it, but chances are high that I will be too lazy to do so. Also, I loading images into the layers because I wanted to achieve a more detailed graphic.

    If you’re interested, here’s a brief – and not very well done – video of the animation: http://www.youtube.com/watch?v=tRkSmJwbbVg

    I wanted to thank you again for this post. I am a total newby to Xcode and Cocoa Touch, although I know how to code in general. To get familiar with the environment I wanted to create a little app and this flip-clock style was one element of it. Your post is the perfect trade-off of guidance vs. encouraging learning. I learned a lot recreating that animation and also understood the concepts behind it. Your code snippets are a very well-balanced guide through the process, but don’t give too much information so that one just copies the code. Really, really great stuff. Seriously. :-)

  • kichigai

    Thanks Ian. This post has helped me a lot.

  • shrey

    hey Ian…

    Thanks alot….. great tutorial for the newbies like me…. :)
    can u please provide us with the full code?? :)

    i am working on the same animation but i dun’t want to show the break in the image on the rotation axis, is it possible???? :-s

    also i want to flip it through Y-axis..just like a book filp…and that too from both the sides, left-to-right and right-to-left…what shoud i do??? :-s

  • http://twitter.com/dhavaltrivedi Dhaval Trivedi

    Hi Ian,

    Can you share the code for – if not anything, just the flip animation of two parts? I’m a newbie and though I somehow did manage to get the top flip animation working, I’m clueless on bottom animation, and changing the content. It’ll be a great help.

    Best,
    Dhaval

  • Jerry

    I love your tutorial, but I’m new to CoreAnimation, could you be so kind to upload the code or project??

  • Tom West

    badass…helped a ton!

  • Nitesh Meshram

    I want to create ipad application just like twitter with full of animation and effect can any one have idea how the transitions and effects takes place in Ipad/Xcode please help me out. Please send me link if possible. Thanks ID- nitesh.meshram@vinfotech.com

  • ludo

    Hi, this project is very interesting, is it possible to have the full code?
    Thanks~~

  • Anonymous

    I have been looking for something like this for ages, and this is absolutely perfect. Thank you so much.

  • ender

    any code to provide ,thx

  • http://www.adhost.dk/sogemaskineoptimering.shtml søgemaskineoptimering københav

    I’m trying to make the flip-clock animation, I find this tutorial very … .voyce.com/index.php/2010/04/10/creating-an-ipad-flip-clock-with-core-animation/ …

  • http://www.adhost.dk/sogemaskineoptimering.shtml søgemaskineoptimering københav

    I’m trying to make the flip-clock animation, I find this tutorial very … .voyce.com/index.php/2010/04/10/creating-an-ipad-flip-clock-with-core-animation/ …

  • Anonymous

    Is there any way you can post the code here?or make a sample app and push it on github? This post doesn’t really explain in depth the implementation, only how the flip animations work, and I’d like to see how this is done from the ground up. I’d really appreciate it! Thanks!

  • http://www.facebook.com/symorium Valery Pavlov

    can you provide sample source code?

  • Anonymous

    Thanks for the great tutorial. The result of this is awesome but I can’t get me head around how exactly I need to implement this, so I can’t get it to work. It would be great if you could share the source code so we can all have a look.

    Thanks

  • Tech

    Any word on the code – I would like to use something similar.

  • Pingback: Looking back at 2011

  • Andrew

    The   most  confusing tutorial  i ever  read ,thank you ;)

  • Francesco

    Hi Ian,thanks for the post..it’s really useful but I had some problem trying to create the full animation. Can you provide the project, please?

  • Paresh Navadiya

    plz i can have source code for tihis i have to develope flip clock

  • MarsAnem

    Awesome tutorial!! thank you for not posting the code heheheh this way I had to think about the code and what it is doing, instead of just copying and pasting n__n

  • Podcaster

    Thank you for the great tutorial, I also would love to see the source code project.

  • http://twitter.com/cbowns Christopher Bowns

    I tossed together a demo project of this technique and posted it to Github: https://github.com/cbowns/flipclock

  • Ramaraju V.N

    Thank you very much.

  • http://cbowns.com/ cbowns

    I tossed together a demo project of this technique and posted it to Github: https://github.com/cbowns/flipclock

  • Follow me on Twitter Follow me on Twitter @voyce

  • Check out Wordz my new fast-paced make-a-word game for iOS.
  • Categories

  • Archives