I hope the following makes some sense and is of some use to newbie Mac Developers out there.
For the past week I have been working on Sub It to try get it ready for first release on the Mac App Store.
One of the tools I have put in is a slider to adjust the movie playback rate – similar to the scrub tool in Final Cut Pro.
There were a few hurdles I had to overcome to get this to work.
Initially I realised I would have to subclass NSSlider to be able to detect/grab the MouseDown and MouseUp NSEvents (I wanted the slider to reset to 0 after the user had released the mouse – putting the movie in pause/stop) – googling revealed that NSSlider runs inside its own event loop in the main thread.
Hooking up a delegate to NSSlider subclass allowed me to call actions/methods in the main controller on those Mouse events – eventually I got this to work with the following code (and via Interface Builder I hooked up the delegate property to the my main Controller):
TimeScrubberSlider.h
#import <Cocoa/Cocoa.h> #import <Foundation/Foundation.h> @interface TimeScrubberSlider : NSSlider { IBOutlet id delegate; } @property (readwrite,retain) id delegate; @end |
TimeScrubberSlider.m
#import "TimeScrubberSlider.h" #import "Controller.h" @implementation TimeScrubberSlider @synthesize delegate; - (void)mouseDown:(NSEvent *)theEvent { [delegate doScrubTime:nil]; [super mouseDown:theEvent]; [self mouseUp:theEvent]; } - (void) mouseUp: (NSEvent*) theEvent { [self setIntValue:0]; [delegate doPlay:nil]; } @end |
This was all working, but the problem was that the time display of the movie would not constantly update – only when the slider was moved would the current time update – not continuously.
So I decided to try attach the updating of the current time display to a separate thread – using NSThread. My thought was that no matter what happened in the main controllers’ thread, this other thread would always do it’s own thing and hopefully not be interrupted.
To get this going I created a method (called ‘launchTimer’) that is called when a file is loaded, and this calls the ‘updateCurrentTime’ method, and this ‘launchTimer’ method is run under the seperate thread. Umm i think that kinda makes sense.
Something along these lines:
snippets from the Controller.m
- (void)setupGUIFromFileRead{ //just a snippet... //begin timer to update the current frame/time display - as separate thread so nsslider "scrub" can play nicely [NSThread detachNewThreadSelector:@selector(launchTimer) toTarget:self withObject:nil]; } -(void)launchTimer{ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSLog(@"startingtimer"); [NSThread setThreadPriority:1.0]; updateTimerToggle = YES; while (updateTimerToggle) { // loop until cancelled [self doUpdateCurrentTimeDisplay:nil]; [NSThread sleepForTimeInterval:.1]; } [pool release]; } |