I am working on an application where I want to call a method every few seconds whilst the user has their finger on a button, and stops on release.
At the moment I am setting off an NSOperation on the Touch Down event, which should then call an NSTimer to fire another NSOperation 2 seconds later.
However only the first “runOperation” is happening; the ones from the timer aren’t.
- (IBAction)buttonPressed:(id)sender
{
[self doStuff];
}
- (void)runOperation {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(doStuff)
object:nil];
[queue addOperation:operation];
[operation release];
}
- (void)doStuff {
/* stuff goes here */
[self performSelectorOnMainThread:@selector(setTimer) withObject:nil waitUntilDone:YES];
}
- (void)setTimer
{
timer = [[NSTimer timerWithTimeInterval:2.f target:self selector:@selector(runOperation) userInfo:nil repeats:NO] retain];
}
- (IBAction)finishTakingPictures:(id)sender {
[timer invalidate];
timer = nil;
}
Man, hope this helps you out, don’t know why I spent so much time on this… 🙂
If you use
NSTimer:timerWithTimeInterval:...you need to add the timer to a run loop usingNSRunLoop:addTimer:forMode. That’s why your timer isn’t firing. If you useNSTimer:scheduledTimerWithTimeInerval:...it will be added to the current run loop.A few thoughts on your implementation.
You have lots of indirection. You can set it directly in
doStuffand get rid of thesetTimermethod.There’s no reason to set the timer on the main thread, you can set it on any thread.
You don’t really need a timer at all, I’ll suggest an alternate implementation below.
ALTERNATE IMPLEMENTATION:
Add a BOOL property e.g.
@property (nonatomic) BOOL isFinished;When the user presses the button, set
isFinished = NO;and start your first operation.When the operation completes, if
isFinished == NOstart another operation. You can add a delay by usingperformSelector:afterDelay:or usingdispatch_after()When the user stops pressing the button, set
isFinished = YESThis alternate implementation assumes you want to start the next operation after the first is complete. If you want the operation to happen after a fixed interval independent of whether an operation is already processing change your timer to repeat and when the timer fires check
isFinished == YESto invalidate the timer.