Cocoa Programming for Mac OS X - Aaron Hillegass [125]
[NSURL fileURLWithPath:@"/Library/Desktop Pictures"]];
}
@end
Before we continue, let’s discuss what we’ve done so far. When the application launches, we will seed the random number generator. Then we configure the view to be layer-hosting by first assigning its layer property and then calling setWantsLayer: with YES. The order of these calls is important. If we had not assigned the layer first, the view would have been configured as layer-backed, which is designed for animating views rather than Core Animation layer hierarchies.
Next, we configure two layers: textContainer and textLayer. The first layer, textContainer, is an instance of CALayer (the most basic layer type) and will create a rounded rectangle filled black with a white border and a shadow. The second layer, textLayer, is a CATextLayer, which not surprisingly can be used to display text. (Note how properties are used extensively in Core Animation to configure the various graphical elements.)
The first layer, textContainer is added to the view’s layer, and textLayer is added to the textContainer (Figure 33.3) . Note that the anchorPoint for each layer is set to (0, 0), which equates to the lower-left corner. The default anchorPoint for a layer is (0.5, 0.5), which is the center of the layer. The anchorPoint governs the position of the layer relative to its position property.
Figure 33.3. Completed Application
Next, setText: is called, which sets the bounds of the layers. The bounds describe the size of the layer; by default, layers have bounds of (0, 0, 0, 0). Layers do have a frame property, like views, but it is more common to set the position and bounds independently.
Next, implement setText:, thumbImageFromImage:, and addImagesFromFolderURL:. While you may not be familiar with the specific APIs being used, you should have a general idea of what they are doing.
- (void)setText:(NSString *)text
{
NSFont *font = [NSFont systemFontOfSize:textLayer.fontSize];
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName, nil];
NSSize size = [text sizeWithAttributes:attrs];
// Ensure that the size is in whole numbers:
size.width = ceilf(size.width);
size.height = ceilf(size.height);
textLayer.bounds = CGRectMake(0, 0, size.width, size.height);
textLayer.superlayer.bounds = CGRectMake(0, 0, size.width + 16,
size.height + 20);
textLayer.string = text;
}
- (NSImage *)thumbImageFromImage:(NSImage *)image
{
const CGFloat targetHeight = 200.0f;
NSSize imageSize = [image size];
NSSize smallerSize = NSMakeSize(targetHeight * imageSize.width /
imageSize.height,
targetHeight);
NSImage *smallerImage = [[NSImage alloc] initWithSize:smallerSize];
[smallerImage lockFocus];
[image drawInRect:NSMakeRect(0, 0, smallerSize.width,
smallerSize.height)
fromRect:NSZeroRect
operation:NSCompositeCopy
fraction:1.0];
[smallerImage unlockFocus];
return smallerImage;
}
- (void)addImagesFromFolderURL:(NSURL *)folderURL
{
NSTimeInterval t0 = [NSDate timeIntervalSinceReferenceDate];
NSFileManager *fileManager = [NSFileManager new];
NSDirectoryEnumerator *dirEnum =
[fileManager enumeratorAtURL:folderURL
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:nil];
int allowedFiles = 10;
for (NSURL *url in dirEnum)
{
// Skip directories:
NSNumber *isDirectory = nil;
[url getResourceValue:&isDirectory
forKey:NSURLIsDirectoryKey
error:nil];
if ([isDirectory boolValue])
continue;
NSImage *image = [[NSImage alloc] initWithContentsOfURL:url];
if (!image)
return;
allowedFiles--;
if (allowedFiles < 0)
break;
NSImage *thumbImage = [self thumbImageFromImage:image];
[self presentImage:thumbImage];
[self setText:[NSString stringWithFormat:@"%0.1fs",
[NSDate timeIntervalSinceReferenceDate] - t0]];
}
}
Finally, implement presentImage: in ScatteredAppDelegate to animate the supplied image onto the view, starting from a tiny speck in the middle and expanding out to a thumbnail version at a random point on the view:
- (void)presentImage:(NSImage