NSTimers Need – (void)pause:(id)sender;
Posted November 25th, 2004 @ 10:49am by Erik J. Barzeski
Consider a Cocoa application I'll call "EggLoop." EggLoop sounds an NSBeep(); every x seconds, where x is set by the user in the application with a slider (from 1 to 999).
This is easily done with a repeating NSTimer. Every x seconds, the NSTimer signals a "beep now" method and resets itself.
Imagine that the user (or the developer) wants to pause the timer. Unfortunately, the developer has to write this himself by keeping track of some number (an integer would be fine for whole seconds). When that int = x, beep and reset the integer to 0. This would require a timer running once per second and triggering a method that does little more than int++
and if(int == x) NSBeep();
.
Solution? Apple should add these:
- (BOOL)pause:(id)sender;
- (BOOL)resume:(id)sender;
The BOOL
could be used to return a NO if the timer was unable to be resumed or paused (perhaps something else invalidated it in the meantime). This is something I considered after titling this entry, as you can see. 🙂
Posted 25 Nov 2004 at 11:08am #
Now, I know I'm getting into hot water by questioning a professional Cocoa developer, but wouldn't it be better to not use an NSTimer for the end event?
I would imagine that you really only need one NSTimer to repeatedly trigger an `increment` method. That increment method would then know when the limit has been reached, to then trigger the `conclude` method (which, I suppose, could itself be an NSNotification posting so that all objects who needed to know when the cycle had ended could be told).
Your pause and resume methods would still be a big benefit, but you'd save yourself the hassle of having to calculate your one-off timer.
Posted 25 Nov 2004 at 11:15am #
The timers are kept separately for other reasons than those listed here. In this very simple case, a solution that both updated the progress bar and dinged when it reached the maximum would work.
I could have written a better example case, I suppose. I rewrote the example above to, hopefully, better illustrate my point. Manual tracking via a method fired every second (or every thousandth of a second, or whatever) should not be necessary.
Posted 25 Nov 2004 at 4:29pm #
Adding two methods as you outlined would be useful in many instances. Have you filed a bug report/feature request with Apple?
Posted 25 Nov 2004 at 11:51pm #
If you didn't mind restarting the cycle on resume, you could implement pause with a call to NSTimer's setFireDate with a value of NSDate's distantFuture.
Resume would be implemented with setFireDate to dateWithTimeIntervalSinceNow, passing the timer's own timeInterval.
These could be implemented in a category on NSTimer.
If you want to handle preserving the remaining delay, you could do it using the timer's userInfo, which could point to a simple object that would maintain and allow setting a float (basically, a mutable NSNumber class). Then you'd use this to preserve the difference between the current time and the fireDate of the timer in the pause method, and use this value in the call to dateWithTimeIntervalSinceNow in resume.
Or you could subclass NSTimer and add an ivar there if you didn't want to use the userInfo.
Posted 26 Nov 2004 at 12:49pm #
Or, Troy, I could ask Apple to implement it as something that should be included in the class so I don't have to worry about it.
Posted 27 Nov 2004 at 4:21am #
Yes this woud be create. I know this from Delphi and i often use and need this.
Posted 27 Nov 2004 at 12:43pm #
I agree that pause and resume might be useful, but I think you still need a better example. The above can be done much more cleanly with -[NSObject performSelector:withObject:afterDelay:]. It also works nicely with your slider since the beep interval can be changed on the fly.
Assume beepInterval is an instance variable of type NSTimeInterval. (NB- Typed in browser, yadda yadda yadda.)
- (void)beepPeriodically
{
NSBeep();
[self performSelector:@selector(beepPeriodically)
withObject:nil
afterDelay:beepInterval];
}
- (void)pause
{
[[self class] cancelPreviousPerformRequestsWithTarget:self
selector:@selector(beepPeriodically)
object:nil];
}
- (void)play
{
[self beepPeriodically];
}
Posted 27 Nov 2004 at 5:07pm #
But even if you get Apple to implement these (and if I were them, I'd seriously consider whether the demand for this was enough to warrant such an API addition, given that NSTimer is a pretty dead-simple API right now), you would then not be able to use it in your software unless you were willing to require whatever new version of the OS included this feature.
Personally, I can't say I've yet had need for such a pair of APIs. But good luck getting them to add it nonetheless.
Posted 27 Nov 2004 at 9:51pm #
Well, Troy, that's a rather silly argument. If everyone took your stance on things, an API would never evolve.
Posted 04 Dec 2004 at 4:54pm #
This came up, more or less, on cocoadev.com, and it was observed that NSTimer basically already has pause and resume.
// pause
[timer setFireDate:[NSDate distantFuture]];
// resume
[timer setFireDate:[NSDate date];
// or if you like,
[timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:[timer timeInterval]]];
Posted 04 Dec 2004 at 5:15pm #
Indeed Ken. Thanks.
Posted 08 Dec 2007 at 2:05pm #
Useful tip Ken, thanks.