CALayer drawLayer:inContext:
Hi all,
I have been trying to wrap my head around this for several days, with lots of Googling and forum searching. Perhaps someone can point me in the right direction.
Basically, I am trying to flash a filled rectangle on the iPhone screen, at coordinates which I will provide. The best way I can come up with to do this is by using a CALayer, modifying its position and bounds, and animating its opacity property. However, I can't seem to wrap my head around it. The Apple docs are not in the least bit helpful, especially when it regards providing custom content to a CALayer instance.
Here is what I have done so far, in a separate test project, to try to completely isolate this problem ("layer" is a class instance variable):
Here's the initWithCoder method:
This is run during initWithCoder:
Documentation I read says that the delegate needs to implement drawLayer:inContext, so I have an additional method in my view with the layer in it:
Here's my drawRect: method in the view, which should produce a red background:
However, the layer only draws in the upper-left corner of the screen (i.e. the origin is 0,0) and the view's drawRect method doesn't get called. I would think this code should draw a 50.0x50.0 rectangle with origin 50, 50, and if I uncomment the layer.hidden=TRUE line it shouldn't display at all. I think I can figure out the animation part once I can simply get the layer to draw itself in the right spot, and for the view to draw itself properly. From what I can tell, I shouldn't need to subclass CALayer to make this work.
Could someone point me in the right direction? Thanks!
[EDIT]: Also, if this is way too complicated for what I'm trying to do, and someone has a better idea, I'm certainly open to suggestions.
I have been trying to wrap my head around this for several days, with lots of Googling and forum searching. Perhaps someone can point me in the right direction.
Basically, I am trying to flash a filled rectangle on the iPhone screen, at coordinates which I will provide. The best way I can come up with to do this is by using a CALayer, modifying its position and bounds, and animating its opacity property. However, I can't seem to wrap my head around it. The Apple docs are not in the least bit helpful, especially when it regards providing custom content to a CALayer instance.
Here is what I have done so far, in a separate test project, to try to completely isolate this problem ("layer" is a class instance variable):
Here's the initWithCoder method:
Code:
- (id)initWithCoder:(NSCoder *)coder {
//we init the class instance the same whether it is loaded from a NIB or not
if (self = [super initWithCoder:coder] ){
[self setupLayers];
[layer setNeedsDisplay];
}
return self;
}
Code:
-(void)setupLayers {
layer = [CALayer layer];
[layer retain];
[[self layer] addSublayer:layer];
//layer.hidden = TRUE;
layer.frame = CGRectMake(50.0, 50.0, 50.0, 50.0);
layer.position = CGPointMake(50.0, 50.0);
NSLog(@"Layer's delegate is %@", [layer delegate]);
float components[4] = {0.25, 0, .68, 1.0/3.0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef blueColor = CGColorCreate( colorSpace, components);
layer.backgroundColor = blueColor;
}
Documentation I read says that the delegate needs to implement drawLayer:inContext, so I have an additional method in my view with the layer in it:
Code:
- (void)drawLayer:(CALayer *)theLayer
inContext:(CGContextRef)theContext{
CGContextSaveGState(theContext);
CGContextSetRGBFillColor(theContext, 0.25, 0.35, 0.45, 0.85);
CGContextFillRect(theContext, layer.bounds);
CGContextRestoreGState(theContext);
}
Here's my drawRect: method in the view, which should produce a red background:
Code:
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextClearRect(ctx, rect);
CGContextSetRGBFillColor(ctx, 1.0, 0.0, 0.0, 1.0);
CGContextFillRect(ctx, rect);
}
However, the layer only draws in the upper-left corner of the screen (i.e. the origin is 0,0) and the view's drawRect method doesn't get called. I would think this code should draw a 50.0x50.0 rectangle with origin 50, 50, and if I uncomment the layer.hidden=TRUE line it shouldn't display at all. I think I can figure out the animation part once I can simply get the layer to draw itself in the right spot, and for the view to draw itself properly. From what I can tell, I shouldn't need to subclass CALayer to make this work.
Could someone point me in the right direction? Thanks!
[EDIT]: Also, if this is way too complicated for what I'm trying to do, and someone has a better idea, I'm certainly open to suggestions.
So, I found a hint here, and after a while, I was able to make it work. It turns out the culprit was that you are not supposed to have the view that contains the layer also serve as the layer's delegate, which happens by default for the UIView's built-in layer. I created a delegate helper object which gets instantiated in the view's setupLayers method.
Here's the code that works, for future CALayer people.
and the delegate object contains one method only:
It's too bad Apple doesn't have any sample code like this on their site - once you realize it's bad to have the view be the delegate of its own layer, which it automatically is, it's pretty straightforward.
Here's the code that works, for future CALayer people.
Code:
-(void)setupLayers {
layerDelegate = [[layerDelegate alloc] init];
layer = [CALayer layer];
[layer retain];
[[self layer] addSublayer:layer];
//layer.hidden = TRUE;
layer.frame = CGRectMake(50.0, 50.0, 50.0, 50.0);
layer.position = CGPointMake(50.0, 50.0);
NSLog(@"Layer's delegate is %@", [layer delegate]);
float components[4] = {0.25, 0, .68, 1.0/3.0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef blueColor = CGColorCreate( colorSpace, components);
layer.backgroundColor = blueColor;
/*****IMPORTANT*****/
[layer setDelegate:layerDelegate];
}
and the delegate object contains one method only:
Code:
- (void)drawLayer:(CALayer *)theLayer
inContext:(CGContextRef)theContext{
CGContextSaveGState(theContext);
CGContextSetRGBFillColor(theContext, 0.25, 0.35, 0.45, 0.85);
CGContextFillRect(theContext, theLayer.bounds);
CGContextRestoreGState(theContext);
}
It's too bad Apple doesn't have any sample code like this on their site - once you realize it's bad to have the view be the delegate of its own layer, which it automatically is, it's pretty straightforward.
One last code fragment, which I hope will help others as they try to do this. Here's an example of the method that flashes the layer. Whenever the method is called, the animation will happen automagically.
Code:
-(IBAction)flashLayer:(id)sender {
//add an explicit animation to our layer
CABasicAnimation* theAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
theAnimation.duration=0.15;
theAnimation.repeatCount=2;
theAnimation.autoreverses=YES;
theAnimation.fromValue=[NSNumber numberWithFloat:0.0];
theAnimation.toValue=[NSNumber numberWithFloat:1.0];
[layer addAnimation:theAnimation forKey:@"animateOpacity"];
}
Possibly Related Threads...
Thread: | Author | Replies: | Views: | Last Post | |
CALayer won't display | Paul from Boston | 1 | 4,476 |
Mar 26, 2010 09:08 AM Last Post: longjumper |
|
UIView tree vs CALayer tree | charshep | 1 | 6,351 |
May 7, 2009 06:09 PM Last Post: longjumper |