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]; } |
Wow Adam, seeing all this code makes my head spin! Amazing that you can get to grips with threading and so. Not the most easy concepts. I wonder if Objective C has any deadlock/lifelock issues in thread synchronization. Keep up the work!
Hey Bart! Ha it makes my head spin too! From what I gathered looking into threading, NSThread / Cocoa makes it pretty simple to implement. I have used threading in python scripts before, and python too seemed to make it seem quite easy to get going. But really, I know very little about it!
Once I get my head around the performance side of things, I am sure I will see some memory leaks and performance hits – will see!
Thanks for your support amigo!
Hey Adam, I’ll Alpha and Beta test it for ya!.