[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:NO];
When repeats: is set to NO, do I need to invalidate the timer inside the specified selector?
Thank you
Edit
Another question, if it self invalidates,
How do you properly cancel a such timer?
Since invalidating already-invalidated timer would crash I assume?
maintain a pointer to the timer and set it to nil inside the selector that will get fired?
No, the timer will invalidate itself
#Eugene if you are using
[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:YES];
then in the selector method you need to give a function like this one
- (void)timerFireMethod:(NSTimer*)theTimer
so when you want to invalidate it you can have a condition like this one
if(workDone == YES)
{
[theTimer invalidate];
}
But if you are using NO in the repeat option then the timer will invalidate itself.
You can maintain flag to save whether the timer has been fired or not.
eg.
BOOL gameOver = NO;
NSTimer * gameOverTimer;
-(void)startGame
{
gameOverTimer = [NSTimer scheduledTimerWithTimeInterval:600 target:self selector:#selector(stopLevel:) userInfo:nil repeats:NO];
// your code
}
-(void)stopLevel:(id)sender
{
gameOver = YES;
// your code
}
-(void)levelFinishedSuccesfully
{
// this method will get called if user finishes the level before your timer ends/stops the level. So the timer is valid and we need to invalidate it
if(!gameOver)
{
[gameOverTimer invalidate];
gameOverTimer = nil;
}
// your code
}
Hope this helps.
If repeats is YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
you are missing to add the timer source to your runloop
addTimer:forMode:
Related
This is what I want to do:
I want a timer, to fire a method and then, in the end of this method, be toggled off, and turn on an other timer on another method, and then entering a loop.
So what are the codes used to toggle between on and off the timer on a method?
In Delphi I use:
timer.enable:=True; // timer.enable:=False;
Are there a similar way to do it on objective-c?
I'm using Xcode 4.4
Thanks!
To turn the timer off, call invalidate on your timer like so:
[yourTimer invalidate]
And then to start a new one:
NSTimer *newTimer;
newTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 //Every how many seconds
target:self
selector:#selector(methodToCall)
userInfo:nil
repeats:YES];
Assuming your NSTimer is called "timer", you can use...
[timer invalidate]
to stop the timer. To make a timer pass a message to it's target method instantly, use
[timer fire]
To start a timer, you use one of the constructor methods listed in the documentation (https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nstimer_Class/Reference/NSTimer.html) such as
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(doThisWhenTimerFires:) userInfo:nil repeats:NO]
- (void)doThisWhenTimerFires:(NSTimer *)timer
{
//code here
}
I'm trying to develop a game and running into a small issue with NSTimer, once a sprite appear it has a certain amount on time in my scene before fading out.
double CALC_TIME = 5;
[NSTimer scheduledTimerWithTimeInterval:CALC_TIME target:self selector:#selector(hideSprite) userInfo:nil repeats:NO];
I want hideSprite to be called after 5 seconds, but instead it's called instantly(Or near instant).
A possible solution:
I know I could do this by setting the timer to repeat, and having a bool firstCall that is set first time and then the next interval the fading is done, the timer is invalidated but I don't think this is good practice
Like this:
bool firstCall = false;
-(void)hideSprite{
if(!firstCall){
firstCall = true
}else{
//fade out sprite
//Invalidate NSTimer
//firstCall = false;
}
}
Thanks for your help!
I suspect something else is calling hideSprite. The code you have written will cause the timer to wait for five seconds before calling the selector hideSprite.
Provide a different selector (write a new test method which just does an NSLog) to the timer and see what happens. This will tell you a) whether the timer is indeed immediately firing and b) if something else is calling hideSprite.
[NSTimer scheduledTimerWithTimeInterval:CALC_TIME target:self selector:#selector(testTimer) userInfo:nil repeats:NO];
-(void) testTimer { NSLog(#"Timer - and only the timer - called me."); }
-(void) hideSprite {
NSLog(#"I definitely wasn't called by the timer.");
}
Quite often it's easier to just use something like
[UIView animateWithDuration: 0 delay: 5 options:0 animations: nil completion:
^{
// fade sprite
}];
(not sure if animations can be nil, but you get the idea).
Consider initializing your timer using the initWithFireDate:interval:target:selector:userInfo:repeats method. Here is a more detailed look at NSTimer.
Currently I am creating a NSTimer that calls my method every one second.
I do this like so:
[NSTimer scheduledTimerWithInterval:1 target:self selector:#selector(clocktick) userInfo:nil repeats:YES];
How can I stop this from calling clocktick?
I have tried assigning it like so: NSTimer *myTimer = [NSTimer ... and then using myTimer = nil; but that did nothing.
You need [myTimer invalidate] that will kill the timer for you.
You have to hold a reference to it, and then send the invalidate message to it:
[myTimer invalidate];
I hope it helps!
Have you tried using 'invalidate' ([timerInstance invalidate]; for example)?
Or is the problem that you don't have a reference to the timer instance anymore?
first of all thanks for getting into my questions.
now, i tried a lot of different ways to call some function in my code but the ways i find it works (performselector:withobject:afterdelay) are get into the function immediately and i need the function to make some "if's" after random times.
Well you can see the code in here-
-(void)playGame{
isButtonPressed=NO;
int minimum=1;
int maximum=4;
int randomTime=(arc4random()%(maximum-minimum))+minimum;
[self performSelector:#selector(ifButtonNotPressed) withObject:nil afterDelay:randomTime];
}
-(void)closingAction{
NSLog(#"Closing Panel automatic");
if (randomFrog==1){
[self performSelector:#selector(animatePanelCloseAction::) withObject:gameImage1_up withObject:gameImage1_down];
}
[self performSelector:#selector(playGame) withObject:nil afterDelay:0.25];
}
-(IBAction)buttonAction:(id)sender{
if (sender == gameButton1 && randomFrog==1){
[self performSelector:#selector(disableAllButtons)];
score=score+1;
isButtonPressed=YES;
[scoreLabel setText:[NSString stringWithFormat:#"Score:%d",score]];
[self performSelector:#selector(closingAction)];
}
}
-(void)ifButtonNotPressed{
if (isButtonPressed==NO){
[self performSelector:#selector(closingAction)];
}
}
as you can see, im trying to check if button was pressed, but its going inside and check it immeditlly and perform it after delay.
how can i call it after real delay ?!
thank you all.
amir.
Try NSTimer to fix this issue.
if(callTimer)
{
[callTimer invalidate];
callTimer=nil;
}
callTimer=[NSTimer scheduledTimerWithTimeInterval:0.50 target:self selector:#selector(callFunction) userInfo:nil repeats:NO];
with help of this you can call any function at particular time interval. if function call again then it invalidates current timer and create new timer for that.
Dont forgot to invalidate timer after calling function means invalidate timer in particular function.
Hey you can use NSTimer class for the same.
- (void)FireTimer : (NSInteger) afterTimeInterval
{
if (afterTimeInterval > 0) //afterTimeInterval is in seconds
{
[NSTimer scheduledTimerWithTimeInterval: afterTimeInterval
target: self
selector: #selector(ResetListAfterExpiration:)
userInfo: nil
repeats: NO];
}
}
- (void)ResetListAfterExpiration: (NSTimer *)timer
{
// your actions
… … …
[timer invalidate]; // must be done to prevent memory leak
}
//Now You call the FireTimer method from where ever you want and it will wait for afterTimeInterval seconds and call the method ResetListAfterExpiration. That's your answer.
I have an IBAction where upon the button being pressed, it creates an unscheduled timer. Then, if that same timer has already started, //do something, else start the timer which was created.
Here is what I have so far:
- (IBAction)button1Press {
NSMethodSignature *sgn = [self methodSignatureForSelector:#selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:#selector(onTick:)];
NSTimer *tapTimer = [NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:NO];
if (/*tapTimer is running*/) {//do something
}else{
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: tapTimer forMode: NSDefaultRunLoopMode];
}
}
My problem is what to put as the condition. If I put tapTimer isValid or != nil then it always returns true, because tapTimer is already declared. I do not want to invalidate or nil out the timer because the main purpose of the button is to only do the action if the button is pressed twice in a time interval of 1 second.
If there is a completely different approach to do what I want then please do tell!
Thanks loads!
From what I understand so far, I see that you are attempting to check whether tapTimer is running or not. I have one suggestion. Use a variable for indicating whether you are having tapTimer running or not. When you run the timer, you change this variable to true, and when the timer's time hits 0 and invoke the method you have selected, you change this variable to false in this method.
Does this help?
I would recommend using a nil check to determine if your timer is running or not.
...
//Define _tapTimer in .h
if (_tapTimer) {//do something
}
else{
_tapTimer = [[NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:NO] retain];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: _tapTimer forMode: NSDefaultRunLoopMode];
}
...
-(void)timerFired:(NSTimer*)timer
{
if(timer == _tapTimer)
{
//Handle timerfired
[_tapTimer release], _tapTimer = nil;
}
}
You can query the RunLoop to know if there is such a timer inside the loop
CFRunLoopRef loopRef = [[runner currentRunLoop] getCFRunLoop];
Boolean timerAdded = CFRunLoopContainsTimer(loopRef, (CFRunLoopTimerRef)timer ,kCFRunLoopDefaultMode)
if (timerAdded)
{
...
}
but, I haven't tested that code yet