Loading the Videos
Once the main user interface is loaded, the important thing is playing the videos. This application opens movie files a lot, so I've written a convenience method for it:
- (QTMovie*)openMovie: (NSString*)fileName { NSNumber *yes = [NSNumber numberWithBool: YES]; NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: fileName, QTMovieFileNameAttribute, yes, QTMovieOpenForPlaybackAttribute, nil]; NSError *error; QTMovie *movie = [[QTMovie alloc] initWithAttributes: attributes error: &error]; if (error) { [window willPresentError: error]; } return movie; }
This method creates a QTMovie instance. You initialize a QTMovie by sending it an initWithAttributes:error: message, with a dictionary as the first argument, describing the properties of the movie.
If you're not familiar with the Foundation framework, you might not be aware of the dictionaryWithObjectsAndKeys: constructor for NSDictionary. This incredibly useful variadic method takes a NULL-terminated list of objects as arguments. These are object-key pairs.
The first key that we set in the attributes dictionary is the filename, which is obviously needed. The second is less obvious. Setting the QTMovieOpenForPlaybackAttribute key means that we're disabling any of the editing functionality of QuickTime. This step is very important on OS X 10.6.
Snow Leopard ships with two versions of QuickTime: QuickTime 7 and QuickTime X. QuickTime 7, which also shipped with OS X 10.5, is an incremental improvement to the QuickTime code that has been under development for the best part of two decades. Unfortunately, this code isn't even remotely 64-bit clean. If you use the QTKit in a 64-bit application with QuickTime 7, another (32-bit) process is spawned, which does the real decoding work and passes the decoded frames back to your main application.
In contrast, QuickTime X is a new 64-bit framework with some other nice features, such as GPU-based acceleration for playback. Unfortunately, it doesn't support editing at all. Unless you explicitly request a playback-only movie, you get the older QuickTime 7 code paths.
Presenting in Full-Screen Mode
One thing that the Flash version of the application can't do is play videos in full-screen mode, which is quite easy with recent versions of OS X. If you wanted full-screen drawing in OS X 10.4 and earlier, you had to use some low-level Core Graphics functions to create a full-screen drawing context. OS X 10.5 added some methods that allow you to tell an NSView to take up the entire screen.
In the video view, we have a button labeled Full Screen that's connected to the -fullScreen: action in our object. This action is quite simple:
- (IBAction)fullScreen: (id)sender { if (isFullScreen) { return; } isFullScreen = YES; [videoView.animator enterFullScreenMode: [window screen] withOptions: nil]; [sender setTitle: @"Exit Full Screen"]; [sender setAction: @selector(exitFullScreen:)]; }
The action first checks whether we're already in full-screen mode. The videoView instance variable is the NSView from the nib file that contains the movie view and controls for playing lessons. We tell the instance variable to enter full-screen mode on the screen that currently contains the window. If you have multiple monitors connected and you want to display on the other monitor, you can simply drag the window between them before entering full-screen mode. Alternatively, you might want to use an explicit screen number. (Keynote does this for the presenter display, for example.)
Although we're using the animator proxy, it doesn't actually do anythingwe hope that this problem will be fixed in a future version of OS X. Meanwhile, you must handle the animation explicitly if you want a nice, smooth transition. You can do this by resizing the window to the screen area when you enter full-screen mode.
Next, we modify the button, making two changes:
- We change the title to "Exit Full Screen." The OS X human interface guidelines recommend that each button be labeled with the action that will be performed when the user presses the button. We could have a button labeled "toggle full screen" or similar, but it's a bit more friendly to say exactly what will happen.
- The other change is to the action message that will be sent when the button is pressed. The next press of the button will send an -exitFullScreen: message to this object. That method looks very similar to -fullScreen:, but performs the inverse operation.
Was It Worth the Trouble?
This application isn't particularly polished. It was written quite quickly, as an example of some of the things that you can do in Cocoa. But it has several feature that the Flash version lacks, such as the ability to jump to specific points in each lesson and to play the videos in full-screen mode. Spending a few hours polishing the visual appearance of the application would easily result in something significantly better than the Flash version.
Of course, the downside is that the Cocoa app is limited to OS X. Because this application uses Core Animation, it won't run with GNUstep (yet) on other platforms.
Whether it's worth writing a dedicated Mac version of your application depends on how much effort it takes relative to the benefit you gain. The effort involved in creating this app was quite smallthe application exists to present videos. The videos are the important bit, and they're the same on all platforms. Of course, in this case, you're unlikely to be watching videos about Cocoa while programming on a Windows machine….