Ole Begemann: iOS Development

iOS SDK, Cocoa and Objective-C

App Store-safe Page Curl animations

with 16 comments

Even if its usefulness is questionable, the page curl has become one of the signature effects of Apple’s iOS devices so it is no surprise that many developers would like to implement this effect in their apps.

iBooks screenshot during page curl

iBooks on the iPad doing a page curl

Apple uses private APIs

The problem is that the page curl animation used by Apple is not exposed in a public and documented API. Steven Troughton-Smith did a great job at documenting how Apple’s implementation works in his post Apple’s iBooks Dynamic Page Curl. Although Steven’s sample code is a bit rough (as Steven himself admits), the inner workings become clear: Apple has written a custom Core Image filter that is accessible with the undocumented kCAFilterPageCurl constant. (Yeah I know, Apple actually tells us in the documentation that “Core Image is not available in iPhone OS”. They lied.)

This filter accepts to input values, inputAngle and inputTime, to control the angle from which the layer is curled up and the magnitude of the curl. For a page curl animation, we would animate inputTime from 0.0f to 1.0f. To attach the filter to a layer, simply add it to an array and assign the array to the layer’s filters property (ignoring that the documentation says this leads to undefined behavior. In this case, undefined behavior is exactly what we want.).

From Steven’s code (edited for clarity):

@class CAFilter;
extern NSString *kCAFilterPageCurl; // From QuartzCore.framework
 
static CAFilter *filter = nil;
 
...
 
// In -touchesMoved:
filter = [[CAFilter filterWithType:kCAFilterPageCurl] retain];
[filter setDefaults];
[filter setValue:[NSNumber numberWithFloat:((NSUInteger)fingerDelta)/100.0] forKey:@"inputTime"];
 
CGFloat _angleRad = angleBetweenCGPoints(currentPos, lastPos);
[filter setValue:[NSNumber numberWithFloat:_angleRad] forKey:@"inputAngle"];
pageView.layer.filters = [NSArray arrayWithObject:filter];

The App Store-safe way

I hope Apple makes this public in the future (and if you want to have it, too, you should file a bug and request it). In the meantime, Tom Brow has written Leaves, a simple component that achieves a page curl effect through a very smart combination of mirrored and shaded layers (for translucent pages) and gradient layers (for shadows). Basically, Tom adds to the layer that contains the page content (topPage):

  1. an overlay to shade the page during the curl animation (topPageOverlay),
  2. a gradient layer that acts as the top page’s shadow during the curl (topPageShadow),
  3. a mirrored image of the page that will be displayed on the back of the topPage layer during the curl (topPageReverseImage),
  4. a nearly-white overlay to soften the topPageReverseImage,
  5. and the page below the current page that will become the new topPage after the curl has finished.

The end result is not quite as stunning as Apple’s solution but it is a very good workaround.

As I played around with Tom’s code (I encourage you to take a look at it, it is very clean), I noticed that LeavesView did not support displaying two pages side by side in landscape mode, so I modified Tom’s code accordingly. At first, I planned to duplicate the entire layer hierarchy for the second page, but then I noticed that even in the side-by-side view it is enough if only the page on the right is animated. It was enough to add a leftPage layer, modify the page skipping algorithm (skip two pages instead of one) and the display of the topPageReverseImage layer (display an image of the next page instead a mirrored image of the current page). This is what you get:

Leaves project page curl screenshot

Page curl in the Leaves project in side-by-side view

The code is not yet perfect: the topPageShadow is not aligned correctly and I struggled a bit with Tom’s implementation of the page image cache so the code in that section is quite rough. Tom has not yet integrated my modifications into his repository but you can already check out my twopages branch (I love GitHub!). When there is time, I hope we can improve it even more.

Update June 21, 2010: John from maniacdev.com made a nice screencast of the effect in action. Thanks!

Written by Ole Begemann

June 17th, 2010 at 8:20 pm

Posted in Uncategorized

16 Responses to 'App Store-safe Page Curl animations'

Subscribe to comments with RSS or TrackBack to 'App Store-safe Page Curl animations'.

  1. [...] App Store-safe Page Curl animations by Ole Begemann [...]

  2. [...] App Store-safe Page Curl animations [...]

  3. [...] Ole’s original post can be found here that provides deeper insight into how the animation is done. [...]

  4. Thanks for this.

    I’d been wanting to recreate this ever since the Keynote, but didn’t have the time to work it out so ended up forgetting about it… great job.

    Maniacdev

    22 Jun 10 at 1:36 am

  5. This is so cool, and i am trying to add cone effect when fliping~~

    Jackie

    22 Jun 10 at 11:37 pm

  6. Oh, very very thanks!!!

    slpo

    2 Jul 10 at 12:34 am

  7. Great work!
    I have read somewhere, that you have managed to implement zooming. Is that true?

    Michal Racko

    25 Jul 10 at 10:46 am

  8. I have been using Tom Brows implementation for a while now and I have been getting little hints from the boss that he would love a two page layout, I have just been too busy, but now I have to say thank you so much for making my life that bit easier. Thumbs up!

    Val

    30 Jul 10 at 6:50 am

  9. it’s been more than a month i have been doing it in openGL ES 2. and with the deadline getting closer, you just saved my life. I was about to commit suicide ;)

    Rick

    1 Aug 10 at 7:42 am

  10. From a quick look at Leaves, it looks like the pages have to be rendered by drawing into a graphics context in the renderPageAtIndex method. I was looking for a way to actually render a view hierarchy as a page. For example, I’d like to put a UIButton on a page, and have the rendering of that button be included in the page curl effect. Am I missing something, or does Leaves not support that?

    Dave

    3 Aug 10 at 4:36 pm

  11. @Michael: no, I have not looked into zooming. Where did you read that? I have to say that I really don’t see the point in zooming because it would destroy the book analogy in my opinion.

    @Dave: yeah, that’s how Leaves works at the moment.

    Ole Begemann

    3 Aug 10 at 10:38 pm

  12. Thanks, guess I’ll tinker around and see what I can get to work.

    BTW, I tried pulling your side-by-side pages version from github, but I keep getting the original single-page version. I was doing this:

    mkdir foo
    cd foo
    git init
    git pull http://github.com/ole/leaves.git

    Am I doing something wrong?

    Dave

    3 Aug 10 at 10:53 pm

  13. As I mention in the post, you have to pull the twopages branch.

    Ole Begemann

    3 Aug 10 at 11:13 pm

  14. @Dave :
    git -b twopages http://github.com/ole/leaves.git

    KPM

    11 Aug 10 at 6:57 pm

  15. Oops, missed the command :)

    git clone -b twopages http://github.com/ole/leaves.git

    KPM

    11 Aug 10 at 6:58 pm

  16. @Ole: I have read that on one forum. Some Italian guy has implemented zooming but with few bugs. And I think I found some discussion about Leaves and read that you replied there. But I could misread.

    Michal Racko

    15 Aug 10 at 10:15 am

Leave a Reply