Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[CHANGE] Use NSOperation and NSOperationQueue to multithread the load…
…ing of images
  • Loading branch information
mattball committed Mar 29, 2009
1 parent b0863e1 commit baf1fe7
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 39 deletions.
22 changes: 22 additions & 0 deletions MBCoverFlowImageLoadOperation.h
@@ -0,0 +1,22 @@
//
// MBCoverFlowImageLoadOperation.h
// MBCoverFlowView
//
// Created by Matt Ball on 3/28/09.
// Copyright 2009 Daybreak Apps. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>

@interface MBCoverFlowImageLoadOperation : NSOperation {
CALayer *_layer;
NSString *_imageKeyPath;
}

@property (nonatomic, retain) CALayer *layer;
@property (nonatomic, copy) NSString *imageKeyPath;

- (id)initWithLayer:(CALayer *)layer imageKeyPath:(NSString *)imageKeyPath;

@end
64 changes: 64 additions & 0 deletions MBCoverFlowImageLoadOperation.m
@@ -0,0 +1,64 @@
//
// MBCoverFlowImageLoadOperation.m
// MBCoverFlowView
//
// Created by Matt Ball on 3/28/09.
// Copyright 2009 Daybreak Apps. All rights reserved.
//

#import "MBCoverFlowImageLoadOperation.h"

#import "NSImage+MBCoverFlowAdditions.h"

@implementation MBCoverFlowImageLoadOperation

@synthesize layer=_layer, imageKeyPath=_imageKeyPath;

- (id)initWithLayer:(CALayer *)layer imageKeyPath:(NSString *)imageKeyPath;
{
if (self = [super init]) {
_layer = [layer retain];
_imageKeyPath = [imageKeyPath copy];
}
return self;
}

- (void)dealloc
{
self.layer = nil;
self.imageKeyPath = nil;
[super dealloc];
}

- (BOOL)isConcurrent
{
return NO;
}

- (void)main
{
@try {
NSImage *image;
NSObject *object = [self.layer valueForKey:@"representedObject"];

if (self.imageKeyPath != nil) {
image = [object valueForKeyPath:self.imageKeyPath];
} else if ([object isKindOfClass:[NSImage class]]) {
image = (NSImage *)object;
}

CGImageRef imageRef = [image imageRef];

CALayer *imageLayer = [[self.layer sublayers] objectAtIndex:0];
CALayer *reflectionLayer = [[imageLayer sublayers] objectAtIndex:0];

imageLayer.contents = (id)imageRef;
reflectionLayer.contents = (id)imageRef;
imageLayer.backgroundColor = NULL;
reflectionLayer.backgroundColor = NULL;
} @catch (NSException *e) {
// If the key path isn't valid, do nothing
}
}

@end
1 change: 1 addition & 0 deletions MBCoverFlowScroller.m
Expand Up @@ -153,6 +153,7 @@ - (void)setNumberOfIncrements:(NSUInteger)newIncrements
} else {
[self setKnobProportion:1.0];
}
[self setNeedsDisplay:YES];
}

/* The documentation for NSScroller says to use -drawArrow:highlight:, but
Expand Down
2 changes: 2 additions & 0 deletions MBCoverFlowView.h
Expand Up @@ -53,6 +53,8 @@
BOOL _autoresizesItems;

NSString *_imageKeyPath;

NSOperationQueue *_imageLoadQueue;
}

/**
Expand Down
58 changes: 23 additions & 35 deletions MBCoverFlowView.m
Expand Up @@ -26,6 +26,7 @@ of this software and associated documentation files (the "Software"), to deal

#import "MBCoverFlowView.h"

#import "MBCoverFlowImageLoadOperation.h"
#import "MBCoverFlowScroller.h"
#import "NSImage+MBCoverFlowAdditions.h"

Expand Down Expand Up @@ -62,7 +63,7 @@ @interface MBCoverFlowView ()
- (float)_positionOfSelectedItem;
- (CALayer *)_newLayer;
- (void)_scrollerChange:(MBCoverFlowScroller *)scroller;
- (void)_refreshLayers;
- (void)_refreshLayer:(CALayer *)layer;
- (CALayer *)_layerForObject:(id)object;
@end

Expand All @@ -79,6 +80,9 @@ @implementation MBCoverFlowView
- (id)initWithFrame:(NSRect)frameRect
{
if (self = [super initWithFrame:frameRect]) {
_imageLoadQueue = [[NSOperationQueue alloc] init];
[_imageLoadQueue setMaxConcurrentOperationCount:1];

_autoresizesItems = YES;

[self setAutoresizesSubviews:YES];
Expand Down Expand Up @@ -240,6 +244,8 @@ - (void)dealloc
self.content = nil;
self.imageKeyPath = nil;
CGImageRelease(_shadowImage);
[_imageLoadQueue release];
_imageLoadQueue = nil;
[super dealloc];
}

Expand Down Expand Up @@ -343,6 +349,10 @@ - (void)resizeSubviewsWithOldSize:(NSSize)oldSize

- (void)setContent:(NSArray *)newContents
{
if ([newContents isEqualToArray:self.content]) {
return;
}

NSArray *oldContent = [self.content retain];

if (_content) {
Expand All @@ -361,6 +371,7 @@ - (void)setContent:(NSArray *)newContents
for (NSObject *object in itemsToAdd) {
CALayer *layer = [self _newLayer];
[layer setValue:object forKey:@"representedObject"];
[self _refreshLayer:layer];
}

// Remove any items which are no longer present
Expand All @@ -373,9 +384,6 @@ - (void)setContent:(NSArray *)newContents

[oldContent release];

// Update the images and indexes
[self _refreshLayers];

[_scroller setNumberOfIncrements:([self.content count]-1)];
self.selectionIndex = self.selectionIndex;
}
Expand Down Expand Up @@ -624,37 +632,17 @@ - (void)_scrollerChange:(MBCoverFlowScroller *)sender
}
}

- (void)_refreshLayers
{
for (CALayer *layer in [_scrollLayer sublayers]) {
CALayer *imageLayer = [[layer sublayers] objectAtIndex:0];
CALayer *reflectionLayer = [[imageLayer sublayers] objectAtIndex:0];

NSObject *object = [layer valueForKey:@"representedObject"];
NSInteger index = [self.content indexOfObject:object];

[layer setValue:[NSNumber numberWithInteger:index] forKey:@"index"];

@try {
NSImage *image;

if (self.imageKeyPath != nil) {
image = [object valueForKeyPath:self.imageKeyPath];
} else if ([object isKindOfClass:[NSImage class]]) {
image = (NSImage *)object;
}

CGImageRef imageRef = [image imageRef];

imageLayer.contents = (id)imageRef;
reflectionLayer.contents = (id)imageRef;
imageLayer.backgroundColor = NULL;
reflectionLayer.backgroundColor = NULL;
} @catch (NSException *e) {
// If the key path isn't valid, move to the next item
continue;
}
}
- (void)_refreshLayer:(CALayer *)layer
{
NSObject *object = [layer valueForKey:@"representedObject"];
NSInteger index = [self.content indexOfObject:object];

[layer setValue:[NSNumber numberWithInteger:index] forKey:@"index"];

// Create the operation
NSOperation *operation = [[MBCoverFlowImageLoadOperation alloc] initWithLayer:layer imageKeyPath:self.imageKeyPath];
[_imageLoadQueue addOperation:operation];
[operation release];
}

- (CALayer *)_layerForObject:(id)object
Expand Down
6 changes: 6 additions & 0 deletions MBCoverFlowView.xcodeproj/project.pbxproj
Expand Up @@ -12,6 +12,7 @@
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
C91E73FC0F79F75C00FD319E /* MBCoverFlowScroller.m in Sources */ = {isa = PBXBuildFile; fileRef = C91E73FB0F79F75C00FD319E /* MBCoverFlowScroller.m */; };
C91E7EE70F7F442F00FD319E /* MBCoverFlowImageLoadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C91E7EE60F7F442F00FD319E /* MBCoverFlowImageLoadOperation.m */; };
C9D1735A0F6B38CD0097827F /* MBCoverFlowView.m in Sources */ = {isa = PBXBuildFile; fileRef = C9D173590F6B38CD0097827F /* MBCoverFlowView.m */; };
C9D1737A0F6B3ADC0097827F /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9D173790F6B3ADC0097827F /* QuartzCore.framework */; };
C9D178230F6F97D60097827F /* MBCoverFlowViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C9D178220F6F97D60097827F /* MBCoverFlowViewController.m */; };
Expand All @@ -31,6 +32,8 @@
8D1107320486CEB800E47090 /* MBCoverFlowView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MBCoverFlowView.app; sourceTree = BUILT_PRODUCTS_DIR; };
C91E73FA0F79F75C00FD319E /* MBCoverFlowScroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBCoverFlowScroller.h; sourceTree = "<group>"; };
C91E73FB0F79F75C00FD319E /* MBCoverFlowScroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBCoverFlowScroller.m; sourceTree = "<group>"; };
C91E7EE50F7F442F00FD319E /* MBCoverFlowImageLoadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBCoverFlowImageLoadOperation.h; sourceTree = "<group>"; };
C91E7EE60F7F442F00FD319E /* MBCoverFlowImageLoadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBCoverFlowImageLoadOperation.m; sourceTree = "<group>"; };
C9D173580F6B38CD0097827F /* MBCoverFlowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBCoverFlowView.h; sourceTree = "<group>"; };
C9D173590F6B38CD0097827F /* MBCoverFlowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBCoverFlowView.m; sourceTree = "<group>"; };
C9D173790F6B3ADC0097827F /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = "<absolute>"; };
Expand Down Expand Up @@ -64,6 +67,8 @@
C9E9ACB50F78B188004DDC0A /* NSImage+MBCoverFlowAdditions.m */,
C91E73FA0F79F75C00FD319E /* MBCoverFlowScroller.h */,
C91E73FB0F79F75C00FD319E /* MBCoverFlowScroller.m */,
C91E7EE50F7F442F00FD319E /* MBCoverFlowImageLoadOperation.h */,
C91E7EE60F7F442F00FD319E /* MBCoverFlowImageLoadOperation.m */,
);
name = Classes;
sourceTree = "<group>";
Expand Down Expand Up @@ -195,6 +200,7 @@
C9D178230F6F97D60097827F /* MBCoverFlowViewController.m in Sources */,
C9E9ACB60F78B188004DDC0A /* NSImage+MBCoverFlowAdditions.m in Sources */,
C91E73FC0F79F75C00FD319E /* MBCoverFlowScroller.m in Sources */,
C91E7EE70F7F442F00FD319E /* MBCoverFlowImageLoadOperation.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
10 changes: 6 additions & 4 deletions MBCoverFlowViewController.m
Expand Up @@ -51,9 +51,7 @@ - (void)awakeFromNib

items = [images copy];

// [(MBCoverFlowView *)self.view setContent:images];

/* NSViewController *labelViewController = [[NSViewController alloc] initWithNibName:nil bundle:nil];
NSViewController *labelViewController = [[NSViewController alloc] initWithNibName:nil bundle:nil];
NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 10)];
[label setBordered:NO];
[label setBezeled:NO];
Expand All @@ -67,7 +65,7 @@ - (void)awakeFromNib
[labelViewController setView:label];
[label release];
[(MBCoverFlowView *)self.view setAccessoryController:labelViewController];
[labelViewController release];*/
[labelViewController release];

[(MBCoverFlowView *)self.view setShowsScrollbar:YES];
}
Expand All @@ -80,6 +78,10 @@ - (void)dealloc

- (void)addItem:(id)sender
{
if ([items isEqualToArray:[(MBCoverFlowView *)self.view content]]) {
return;
}

NSArray *content = [items subarrayWithRange:NSMakeRange(0, [[(MBCoverFlowView *)self.view content] count]+1)];
[(MBCoverFlowView *)self.view setContent:content];
}
Expand Down

0 comments on commit baf1fe7

Please sign in to comment.