





















































In this recipe, we will use multiple layers to draw our custom view. Using a delegate method, we will draw the background of our view. A second layer will be used to include an image centered in the view. When complete, the sample application will resemble the following screenshot:
In Xcode, create a new Cocoa Application and name it CALayer.
#import <QuartzCore/QuartzCore.h>
- (void) awakeFromNib {
CALayer *largeLayer = [CALayer layer];
[largeLayer setName:@"large"];
[largeLayer setDelegate:self];
[largeLayer setBounds:[self bounds]];
[largeLayer setBorderWidth:4.0];
[largeLayer setLayoutManager:[CAConstraintLayoutManager
layoutManager]];
CALayer *smallLayer = [CALayer layer];
[smallLayer setName:@"small"];
CGImageRef image = [self convertImage:[NSImage
imageNamed:@"LearningJQuery"]];
[smallLayer setBounds:CGRectMake(0, 0, CGImageGetWidth(image),
CGImageGetHeight(image))];
[smallLayer setContents:(id)image];
[smallLayer addConstraint:[CAConstraint
constraintWithAttribute:kCAConstraintMidY
relativeTo:@"superlayer"
attribute:kCAConstraintMidY]];
[smallLayer addConstraint:[CAConstraint
constraintWithAttribute:kCAConstraintMidX
relativeTo:@"superlayer"
attribute:kCAConstraintMidX]];
CFRelease(image);
[largeLayer addSublayer:smallLayer];
[largeLayer setNeedsDisplay];
[self setLayer:largeLayer];
[self setWantsLayer:YES];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
CGContextSetRGBFillColor(context, .5, .5, .5, 1);
CGContextFillRect(context, [layer bounds]);
}
- (CGImageRef) convertImage:(NSImage *)image {
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef
)[image TIFFRepresentation],
NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0,
NULL);
CFRelease(source);
return imageRef;
}
- (CGImageRef) convertImage:(NSImage *)image;
We are creating two layers for our view. The first layer is a large layer, which will be the same size as our MyView view. We set the large layers delegate to self so that we can draw the layer in the drawLayer: delegate method. The drawLayer: delegate method simply fills the layer with a mid-gray color. Next, we set the bounds property and a border width property on the larger layer.
Next, we create a smaller layer whose contents will be the image that we included in the project. We also add a layout manager to this layer and configure the constraints of the layout manager to keep the smaller layer centered both horizontally and vertically relative to the larger view using the superlayer keyword.
Lastly, we set the small layer as a sub-layer of the large layer and force a redraw of the large layer by calling setNeedsDisplay. Next, we set the large layer as the MyView's layer. We also need to call the setWantsLayer:YES on the MyView to enable the use of layers in our view.
Since we used a layout manager to center the image in the view, the layout manager will also handle the centering of the image when the user resizes the view or window. To see this in action, modify the Size properties in Interface Builder for the custom view as shown in the screenshot below:
Cocoa provides a way to animate views by changing properties using implied animations. In this recipe, we will resize our custom view when the resize button is clicked, by changing the views frame size.
In Xcode, create a new Cocoa Application and name it ChangingProperties.
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:[
self bounds] xRadius:8.0 yRadius:8.0];
[path setClip];
[[NSColor whiteColor] setFill];
[NSBezierPath fillRect:[self bounds]];
[path setLineWidth:3.0];
[[NSColor grayColor] setStroke];
[path stroke];
#import "MyView.h"
NSButton *button;
MyView *myView;
@property (assign) IBOutlet NSButton *button;
@property (assign) IBOutlet MyView *myView;
- (IBAction) resizeButtonHit:(id)sender;
@synthesize button;
@synthesize myView;
static BOOL isSmall = YES;
- (IBAction) resizeButtonHit:(id)sender {
NSRect small = NSMakeRect(20, 250, 150, 90);
NSRect large = NSMakeRect(20, 100, 440, 240);
if (isSmall == YES) {
[[myView animator] setFrame:large];
isSmall = NO;
} else {
[[myView animator] setFrame:small];
isSmall = YES;
}
}
We define two sizes for our view, one small size that is the same as the initial size of the view, and one large size. Depending on the state of the isSmall global variable, we set the view's frame size to one of our predefined sizes. Note that we set the view's frame via the views animator property. By using this property, we make use of the implicit animations available in the view.
Using the same technique, we can animate several other properties of the view such as its position or opacity. For more information on which properties can be implicitly animated, see Apple's Core Animation Programming Guide.