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).
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.
Have you tried making a parent layer above the OpenGL layer and animating that instead?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With