Providing CALayer Content
Positioning Content Within a Layer
When using Cocoa views you must subclass NSView
or UIView
and implement drawRect:
in order to display anything. However CALayer
instances can often be used directly, without requiring you to create a subclass. Because CALayer
is a key-value coding compliant container class, that is you can store arbitrary values in any instance, subclassing can often be avoided entirely.
You specify the content of a CALayer
instance in one of the following ways:
Explicitly set the contents
property of a layer instance using a CGImageRef
that contains the content image.
Specify a delegate that provides, or draws, the content.
Subclass CALayer
and override one of the display methods.
A layer’s content image is specified by contents
property to a CGImageRef
. This can be done from another object when the layer is created (as shown in Table 3) or at any other time.
Listing 4 Setting a layer’s contents property
CALayer *theLayer; |
// create the layer and set the bounds and position |
theLayer=[CALayer layer]; |
theLayer.position=CGPointMake(50.0f,50.0f); |
theLayer.bounds=CGRectMake(0.0f,0.0f,100.0f,100.0f); |
// set the contents property to a CGImageRef |
// specified by theImage (loaded elsewhere) |
theLayer.contents=theImage; |
You can draw content for your layer, or better encapsulate setting the layer’s content image by creating a delegate class that implements one of the following methods: displayLayer:
or drawLayer:inContext:
.
Implementing a delegate method to draw the content does not automatically cause the layer to draw using that implementation. Instead, you must explicitly tell a layer instance to re-cache the content, either by sending it a setNeedsDisplay
or setNeedsDisplayInRect:
message, or by setting its needsDisplayOnBoundsChange
property to YES
.
Delegates that implement the displayLayer:
method can determine which image should be displayed for the specified layer, and then set that layer’s contents
property accordingly. The example in implementation of displayLayer:
in “Layer Coordinate System” sets the contents
property of theLayer
depending on the value of the state key. Subclassing is not required to store the state
value, as the CALayer
instance acts as a key-value coding container.
Listing 5 Example implementation of the delegate method displayLayer:
- (void)displayLayer:(CALayer *)theLayer |
{ |
// check the value of the layer's state key |
if ([[theLayer valueForKey:@"state"] boolValue]) |
{ |
// display the yes image |
theLayer.contents=[someHelperObject loadStateYesImage]; |
} |
else { |
// display the no image |
theLayer.contents=[someHelperObject loadStateNoImage]; |
} |
} |
If you must draw the layer’s content rather than loading it from an image, you implement the drawLayer:inContext:
delegate method. The delegate is passed the layer for which content is required and a CGContextRef
to draw the content in.
The example in implementation of drawLayer:inContext::
in “Specifying a Layer’s Geometry” draws a path in using the lineWidth
key value returned by theLayer
.
Listing 6 Example implementation of the delegate method drawLayer:inContext:
- (void)drawLayer:(CALayer *)theLayer |
inContext:(CGContextRef)theContext |
{ |
CGMutablePathRef thePath = CGPathCreateMutable(); |
CGPathMoveToPoint(thePath,NULL,15.0f,15.f); |
CGPathAddCurveToPoint(thePath, |
NULL, |
15.f,250.0f, |
295.0f,250.0f, |
295.0f,15.0f); |
CGContextBeginPath(theContext); |
CGContextAddPath(theContext, thePath ); |
CGContextSetLineWidth(theContext, |
[[theLayer valueForKey:@"lineWidth"] floatValue]); |
CGContextStrokePath(theContext); |
// release the path |
CFRelease(thePath); |
} |
Although often unnecessary, you can subclass CALayer
and override the drawing and display methods directly. This is typically done when your layer requires custom behavior that can’t be provided though delegation.
A subclass can override the CALayer
display method and set the layer’s contents to the appropriate image. The example in “Transforming a Layer’s Geometry”provides the same functionality as the delegate implementation of displayLayer:
in “Layer Coordinate System.” The difference is that the subclass defines state
as instance property, rather than depending on the key-value coding container ability of CALayer
.
Listing 7 Example override of the CALayer display method
- (void)display |
{ |
// check the value of the layer's state key |
if (self.state) |
{ |
// display the yes image |
self.contents=[someHelperObject loadStateYesImage]; |
} |
else { |
// display the no image |
self.contents=[someHelperObject loadStateNoImage]; |
} |
} |
CALayer
subclasses can draw the layer’s content into a graphics context by overriding drawInContext:
. The example in “Modifying the Transform Data Structure” produces the same content image as the delegate implementation in “Specifying a Layer’s Geometry.” Again, the only difference in the implementation is that lineWidth
and lineColor
are now declared as instance properties of the subclass.
Listing 8 Example override of the CALayer drawInContext: method
- (void)drawInContext:(CGContextRef)theContext |
{ |
CGMutablePathRef thePath = CGPathCreateMutable(); |
CGPathMoveToPoint(thePath,NULL,15.0f,15.f); |
CGPathAddCurveToPoint(thePath, |
NULL, |
15.f,250.0f, |
295.0f,250.0f, |
295.0f,15.0f); |
CGContextBeginPath(theContext); |
CGContextAddPath(theContext, thePath ); |
CGContextSetLineWidth(theContext, |
self.lineWidth); |
CGContextSetStrokeColorWithColor(theContext, |
self.lineColor); |
CGContextStrokePath(theContext); |
CFRelease(thePath); |
} |
Subclassing CALayer
and implementing one of the drawing methods does not automatically cause drawing to occur. You must explicitly cause the instance to re-cache the content, either by sending it a setNeedsDisplay
or setNeedsDisplayInRect:
message, or by setting its needsDisplayOnBoundsChange
property to YES
.
The CALayer
property contentsGravity
allows you to position and scale the layer’s contents
image within the layer bounds. By default, the content image fills the layer’s bounds entirely, ignoring the natural aspect ratio of the image.
Using the contentsGravity
positioning constants you can specify that the image is placed along any of the layer’s edges, in the layer’s corners, or centered within the layer’s bounds. “Specifying a Layer’s Geometry” lists the positioning constants and their corresponding positions.
Position constant | Description |
---|---|
| Positions the content image in the top left corner of the layer. |
| Positions the content image horizontally centered along the top edge of the layer. |
| Positions the content image in the top right corner of the layer. |
| Positions the content image vertically centered on the left edge of the layer. |
| Positions the content image at the center of the layer. |
| Positions the content image vertically centered on the right edge of the layer. |
| Positions the content image in the bottom left corner of the layer. |
| Positions the content image centered along the bottom edge of the layer. |
| Positions the content image in the top right corner of the layer. |
“Layer Coordinate System” shows the supported content positions and their corresponding constants.
The content image can be scaled up, or down, by setting the contentsGravity
property to one of the gravity constants listed in “Transform Functions”
Scaling constant | Description |
---|---|
| Resize the content image to completely fill the layer bounds, potentially ignoring the natural aspect of the content. This is the default. |
| Resize the content image to scale such that it is displayed as large as possible within the layer bounds, yet still retains its natural aspect. |
“Transforming a Layer’s Geometry” illustrates how a square image is resized to fit within a rectangular layer bounds using the resizing modes.
© 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-11-13)