Drawing with CADisplayLink

For 90% of the animations you’ll ever need to do CoreAnimation or UIView animations are enough. Sometimes, the thing you want to animate can’t be handled by simple property based transitions or by moving an object along a path. Enter CADisplayLink:

From the CADisplayLink class reference:

A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.

The purposes that I need, I want to animate a circle similarly to how flex charts draws pie charts (a “swipe open” kind of animation). For the demo, though, I’ll simply use the CADisplayLink to animate a line extending across the screen on a CALayer using it’s drawInContext: method.

First the main view that contains my CALayer subclass:

//  MainView.h

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "AnimationLayer.h"

@interface MainView : UIView 
{
    AnimationLayer *alayer;
}

@end
//  MainView.m

#import "MainView.h"

@implementation MainView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)didMoveToSuperview
{
    if ([self superview])
    {
                
        alayer = [[AnimationLayer alloc] init];
        alayer.frame = self.frame;
        [self.layer addSublayer:alayer];
        
    }
}

And next the CALayer that will be animated:

//  AnimationLayer.h

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

@interface AnimationLayer : CALayer 
{
    CADisplayLink *displayLink;
}

@end
//  AnimationLayer.m
#import "AnimationLayer.h"

static bool _running;

@implementation AnimationLayer

- (id)init
{
    self = [super init];
    if (self)
    {
        displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)] retain];
        [self setNeedsDisplayOnBoundsChange:YES];
    }
    
    return self;
}

static CGPoint lastPoint = {0, 0};
- (void)drawInContext:(CGContextRef)ctx
{
    if (!_running)
    {
        [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        _running = YES;
        return;
    }

    CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
    
    CGRect rect = CGContextGetClipBoundingBox(ctx);
    
    CGContextMoveToPoint(ctx, 0, 0);
    lastPoint.x = lastPoint.y += 1;
    CGContextAddLineToPoint(ctx, lastPoint.x, lastPoint.y);
    CGContextStrokePath(ctx);
    
    if (lastPoint.x == rect.size.width)
    {
        [displayLink invalidate];
        _running = NO
    }
}

@end

The main view is pretty uninteresting as it only creates a new CALayer and adds it as a sublayer of itself.

The animation layer holds the CADisplayLink as an Ivar and initializes it with the layer’s drawInContext: method.

We need some kind of flag to tell the CADisplayLink when to stop updating, for this I used a static BOOL _running. On the first pass of drawInContext, we’ll add the display link to the main run loop, at which point it will start calling the specified selector. We’ll set the _ruinning flag to true and return. On every subsequent pass of drawInContext: we’ll draw a longer line running diagonally. At the end we’ll check the condition to invalidate the CADisplayLink; in this case I’ll check to see whether the line has fully crossed the screen. Once we invalidate the display link the animation will stop.

This is a simple example, but it shows how you can animate an object using pure drawing for the times when property animations simply won’t do.

Advertisements

Posted on March 9, 2011, in Code and tagged , , , , . Bookmark the permalink. 3 Comments.

  1. If you are retaining the display link then you should be releasing it in a dealloc. Also there’s a bit of a question of whether you can legitimately store the display link pointer in the same class as you provide to it as a target, because displaylink retains the self object that you provide, so you have a retain loop, and therefore your animationlayer object cannot be deallocated.

  1. Pingback: More CADisplayLink « Pat Zearfoss

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: