Continuing from Part I of the IKImageBrowserView tutorial, I’m going to show you how to extend the project to do things that we shouldn’t really do, but are so much fun it’s impossible to resist. And really, when has anyone ever gotten in trouble for throwing caution to the wind?

In this part of the tutorial we’ll take the previous project and modify it so that you can turn your IKImageBrowserView into a Cover Flow view AND we’ll add Quicklook functionality to the view. I caution you that we’ll be using private secure API’s so do not use this in a shipping application and do not hold me responsible for any problems on future releases of the OS (this has been tested on 10.5.2). This tutorial is for education purposes only… blah, blah, blah.

Getting Started

If you haven’t already, take a look at the previous tutorial. It discusses the original project setup and custom classes needed to make the IKImageBrowserView work. These same core classes will be used again, with very minor modifications. We’ll then add in a new subclass to control the Cover Flow view. The completed project can be downloaded here.

Changes to the existing application

The following changes have been made to the previous project:

IKBController.h - Changed the browserView type to id.
IKBController.m - In the Data source methods changed the method names
		  to reflect the use of imageFlow instead of imageBrowser.
		  The implementations are identical otherwise.
		- In performDragOperation, the imageID is set to the full
		  file name rather than just the last path component.
IKBBrowserItem.h- Added in an instance variable with the name fullImagePath.
		  Modified the property to include an accessor for fullImagePath.
IKBBrowserItem.m- Modified the init routine to initialize fullImagePath.

Security Class Dump Headers

Most of the code in this portion of the tutorial is derived from: http://ciaranwal.sh/2007/12/07/quick-look-apis.

To make compiling proceed a bit more smoothly, we need to make the headers available for IKImageFlowView (our Cover Flow view we’ll be subclassing) and Quicklook. Using class-dump, header files were generated for each.

Cover Flow Subclass

The subclass we’ll be using for Cover Flow is called IKBImageFlowView. I’ve subclassed it to make integration with Quicklook a bit more transparent. The subclass itself does nothing important other than to override the keyDown event method in NSResponder. This method looks like:


- (void)keyDown:(NSEvent *)event
{	
	[super keyDown:event];
	
	if([[event charactersIgnoringModifiers] characterAtIndex:0] == ' ')
		[self userDidPressSpaceInOutlineView:self];
	else if([[event charactersIgnoringModifiers] characterAtIndex:0] == NSRightArrowFunctionKey)
		[self userDidPressLeftRightInView:self];
	else if([[event charactersIgnoringModifiers] characterAtIndex:0] == NSLeftArrowFunctionKey)
		[self userDidPressLeftRightInView:self];
	
}

What we are doing is this. First take the keyDown event and let the parent class handle it. We want to do this so that the previous functionality built into the view is maintained while Quicklook browsing is open (what this means will become more apparent later).

Next we look to see what key was pressed and perform the corresponding action. In Leopard hitting the space bar in Quicklook enabled views causes Quicklook to launch. If a Quicklook view is open already, pressing the the left of right key will cause the view to scroll and the view will automatically update to the new Finder selection.

Easy so far…

Quicklook

Next we need to handle the actual Quicklook functionality. First we start by handling the type of key press.

For spacebar we do:


- (void)userDidPressSpaceInOutlineView:(id)anOutlineView
{
	// If the user presses space when the preview panel is open then we close it
	if([[QLPreviewPanel sharedPreviewPanel] isOpen])
		[[QLPreviewPanel sharedPreviewPanel] closeWithEffect:2];
	else
	{
		[self updateQuicklook];
	}
}

And for arrow keys we do:


- (void)userDidPressLeftRightInView:(id)anOutlineView
{
	if([[QLPreviewPanel sharedPreviewPanel] isOpen])
	{
		[self updateQuicklook];
	}
}

Nothing too fancy.

Each of these methods calls updateQuicklook:


- (void)updateQuicklook
{
	// Otherwise, set the current items
	[self quickLookSelectedItems:[self selectedIndex]];
	
	// And then display the panel
	[[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect:2];
	
	// Restore the focus to our window to demo the selection changing, scrolling 
	// (left/right) and closing (space) functionality
	[[self window] makeKeyWindow];
}

Again nothing fancy. But this in turn calls quickLookSelectedItems where the work for getting the proper information to display is done:


- (void)quickLookSelectedItems:(int)itemIndex
{
	NSMutableArray * browserData = [[self dataSource] browserData];
	
	NSMutableArray* URLs   = [NSMutableArray arrayWithCapacity:[browserData count]];
	
	NSURL * fileURL = [NSURL fileURLWithPath:[[browserData objectAtIndex:itemIndex] fullImagePath]];
	[URLs addObject:fileURL];
	
	// The code above just gathers an array of NSURLs representing the selected items,
	// to set here
	[[QLPreviewPanel sharedPreviewPanel] setURLs:URLs currentIndex:0 preservingDisplayState:YES];
	
}

Ok… First. We need access to the data that the Cover Flow is using to render. This is our NSMutableArray that contains IKBBrowserItem’s. Recall from the previous tutorial that an IKBBrowserItem conforms to the informal protocol for IKImageBrowserItem. The same type of information is used to render the file icons in Cover Flow as it is in ImageBrowser views. But also recall we modified the subclass to provide the full path to file on disk. We did this so that Quicklook knows where to look when generating the preview of the files contents.

Next we allocate some space for file URL’s, get the file path and convert it to an NSURL and populate the array with the currently selected file path. And finally, we tell Quicklook to do set the URLs. At this point Quicklook has all of the information it needs to do its job.

Interface Builder

In IB, the IKBrowserView was deleted and a Custom View added. The Custom View class was set to IKBImageFlowView. And the outlet from the application controller was re-established.

Build and Go

Back in Xcode hit Build and Go. After the program launches you’ll see the new Cover Flow window. Drag and Drop some files into the view. At this point you should be able to browse the view with the scroll bar, but also with the arrow keys. Hit the space bar and Quicklook should generate a preview for the file. If it knows how to read that file type, it will show the files contents; otherwise Quicklook will display the icon and file information (just like in Finder). But now note, that if you keep Quicklook open and press the right/left arrow keys two things will happen:

– The Cover Flow view in the background will change selections
– The Quicklook view will update to the new file

All of this can be done a bit more compactly, of course. But for clarity in breaking down the process each step has been moved to a different method. Also note, that in quickLookSelectedItems: this line:


[[QLPreviewPanel sharedPreviewPanel] setURLs:URLs currentIndex:0 preservingDisplayState:YES];

A more efficient way to set this up, is when the drag occurs, set all of the URLs for the files dropped at that time. Then when Quicklook is activated, simply set the current index using setIndexOfCurrentURL.

So that’s it… Again, this is all for fun and education. Don’t do this in a shipping application.