Take the 2-minute tour ×

Edit: I suppose instead of the long explanation below I might also ask: Sending -setNeedsDisplay to an instance of CAEAGLLayer does not cause the layer to redraw (i.e., -drawInContext: is not called). Instead, I get this console message:

<GLLayer: 0x4d500b0>: calling -display has no effect.

Is there a way around this issue? Can I invoke -drawInContext: when -setNeedsDisplay is called? Long explanation below:

I have an OpenGL scene that I would like to animate using Core Animation animations.

Following the standard approach to animate custom properties in a CALayer, I created a subclass of CAEAGLLayer and defined a property sceneCenterPoint in it whose value should be animated. My layer also holds a reference to the OpenGL renderer:

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

@interface GLLayer : CAEAGLLayer
    ES2Renderer *renderer;

@property (nonatomic, retain) ES2Renderer *renderer;
@property (nonatomic, assign) CGPoint sceneCenterPoint;

I then declare the property @dynamic to let CA create the accessors, override +needsDisplayForKey: and implement -drawInContext: to pass the current value of the sceneCenterPoint property to the renderer and ask it to render the scene:

#import "GLLayer.h"

@implementation GLLayer

@synthesize renderer;
@dynamic sceneCenterPoint;

+ (BOOL) needsDisplayForKey:(NSString *)key
    if ([key isEqualToString:@"sceneCenterPoint"]) {
        return YES;
    } else {
        return [super needsDisplayForKey:key];

- (void) drawInContext:(CGContextRef)ctx
    self.renderer.centerPoint = self.sceneCenterPoint;
    [self.renderer render];

(If you have access to the WWDC 2009 session videos, you can review this technique in session 303 ("Animated Drawing")).

Now, when I create an explicit animation for the layer on the keyPath @"sceneCenterPoint", Core Animation should calculate the interpolated values for the custom properties and call -drawInContext: for each step of the animation:

- (IBAction)animateButtonTapped:(id)sender
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"sceneCenterPoint"];
    animation.duration = 1.0;
    animation.fromValue = [NSValue valueWithCGPoint:CGPointZero];
    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0f, 1.0f)];
    [self.glView.layer addAnimation:animation forKey:nil];

At least that is what would happen for a normal CALayer subclass. When I subclass CAEAGLLayer, I get this output on the console for each step of the animation:

2010-12-21 13:59:22.180 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
2010-12-21 13:59:22.198 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
2010-12-21 13:59:22.216 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
2010-12-21 13:59:22.233 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.

So it seems that, possibly for performance reasons, for OpenGL layers, -drawInContext: is not getting called because these layers do not use the standard -display method to draw themselves. Can anybody confirm that? Is there a way around it?

Or can I not use the technique I laid out above? This would mean I would have to implement the animations manually in the OpenGL renderer (which is possible but not as elegant IMO).

share|improve this question
add comment

2 Answers

up vote 2 down vote accepted

Have you tried making a parent layer above the OpenGL layer and animating that instead?

share|improve this answer
Thanks for the suggestion. I'll try it out and report back. –  Ole Begemann Dec 27 '10 at 12:03
It indeed works that way, thanks a lot! Performance does not seem to be as good as a pure OpenGL solution but I am gonna write a blog post about the specifics soon. –  Ole Begemann Dec 27 '10 at 15:30
add comment

You can override display instead of drawInContext. During the animation, the animated value is in the presentation layer.

- (void) display
    GLLayer* myPresentationLayer = (GLLayer*)[self presentationLayer];
    self.renderer.centerPoint = myPresentationLayer.sceneCenterPoint;
    [self.renderer render];

At the end the presentation layer will have the model layer value, so you’ll need to set the final value on the model layer before starting the animation.

share|improve this answer
Great suggestion, Jeff, thanks. I tried it out and this approach works, too. Performance-wise, it seems to be in the same ballpark as Dad's idea. –  Ole Begemann Jan 6 '11 at 21:14
add comment

Your Answer


