How can I create an NSTimer that repeats twice and then stops - iphone

I'd like to create an NSTimer that repeats twice then stops and invalidates itself. I'd rather not use a loop if that's possible. Any thoughts on how I could do this?

Create a static int inside your timer delegate function that is initialized to 0.
Increment it each time the delegate is called.
When the counter reaches the value you wish invalidate the timer.

This is something your timer's target should handle, not something the timer itself should handle. You can either install a repeating timer and have the target invalidate it the second time it fires, or you can install a one-shot timer, reinstall it after the first time it fires, and then not set it up again the second time.

Basically, you need a state machine state variable that can be accessed both from the routine that initializes the timer, and from the timer's target.
Set the state variable to allow the first call to the timer task to restart the timer, but in that call also set that state variable so that subsequent calls do not restart.
Note that this kind of state variable can be used for any number of timer task repetitions, by simply decrementing it.
State machines are pretty much how all (synchronous) digital chips and logic works.

I very much disagree with the Jeremy that this is something that the target should handle. In fact I disagree so much that I have created my own Timer class, based on NSTimer, that you can configure in detail.
- (void) doSomething: (Timer*) timer
{
NSLog(#"This is iteration %d", timer.currentIteration);
}
- (void) startDoingSomething
{
Timer* timer = [Timer new];
timer.interval = 5.0; // Fire every 5 seconds
timer.delay = 2.5; // Start firing after 2.5 seconds
timer.iterations = 3; // Only fire three times
timer.target = self;
timer.selector = #selector(doSomething:);
[timer schedule];
// Don't forget to release timer somewhere - the above is just an example
}
See http://github.com/st3fan/ios-utils

One solution might look similar to this.
Launching the timer
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(timerMethod:) userInfo:nil repeats:YES];
Handling the timer and repetitions
int repetitions = 2; //EDIT: remove static declaration (see below)
- (void)timerMethod:(NSTimer*)theTimer{
NSLog(#"Timer fired");
repetitions--;
if(repetitions == 0){
[theTimer invalidate];
NSLog(#"Timer stopped");
}
}
EDIT:
I removed the static modifier above to make a more generic example. The original intent of the static was to persist the timer across objects of similar type, a request that the OP did not make.

Related

objective-c: Animate button before timer ends

I'm working on a very simple iPhone game that involves choosing the right colored button as many times in a row based on a randomized voice prompt. I have it set up so that if the button is one color and gets clicked, it always goes to a hard-coded color every time (e.g. if you click red, it always turns blue). The color change method is set up in an IBOutlet. I have a timer set up in a while loop, and when the timer ends it checks if the player made the right selection. The problem is that the button color change does not occur until after the timer runs out, and this causes a problem with the method used to check the correct answer. Is there a way to make this color change happen instantly? From what I've searched I know it has something to do with storyboard actions not occurring until after code executes, but I haven't found anything with using a timer. Here is a section of the method that calls the timer if the answer is correct:
BOOL rightChoice = true;
int colorNum;
NSDate *startTime;
NSTimeInterval elapsed;
colorNum = [self randomizeNum:middle];
[self setTextLabel:colorNum];
while (rightChoice){
elapsed = 0.0;
startTime = [NSDate date];
while (elapsed < 2.0){
elapsed = [startTime timeIntervalSinceNow] * -1.0;
NSLog(#"elapsed time%f", elapsed);
}
rightChoice = [self correctChoice:middleStatus :colorNum];
colorNum = [self randomizeNum:middle];
}
One of two things stood out
You're using a while loop as a timer, don't do this - the operation is synchronous.
If this is run on the main thread, and you code doesn't return, your UI will update. The mantra goes: 'when you're not returning you're blocking.'
Cocoa has NSTimer which runs asynchronously - it is ideal here.
So let's get to grips with NSTimer (alternatively you can use GCD and save a queue to an ivar, but NSTimer seems the right way to go).
Make an ivar called timer_:
// Top of the .m file or in the .h
#interface ViewController () {
NSTimer *timer_;
}
#end
Make some start and stop functions. How you call these is up to you.
- (void)startTimer {
// If there's an existing timer, let's cancel it
if (timer_)
[timer_ invalidate];
// Start the timer
timer_ = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(onTimerFinish:)
userInfo:nil
repeats:NO];
}
- (void)onTimerFinish:(id)sender {
NSLog(#"Timer finished!");
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
- (void)stopTimer {
if (!timer_)
return;
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
And now
Put your timer test code in the onTimerFinish function.
Make an ivar that stores the current choice. Update this ivar when a choice is made and make the relevant changes to the UI. Call stopTimer if the stop condition is met.
In the onTimerFinished you can conditionally call and startTimer again if you desire.
Hope this helps!

iPhone NSTimer change repeat time

I have a static method in which I setup NSTimer, but sometimes I need to change repeat time. How to do that if I work with static method? Should be something like this:
+ (void)setupTimer:(BOOL)updateTime newTime:(int)time {
NSTimer *timer;
if (updateTime) {
[timer invalidate];
timer = nil;
}
timer = [NSTimer scheduledTimerWithTimeInterval:time
target:self
selector:#selector(myMethod:)
userInfo:nil
repeats:YES];
}
But it's not working...and I know that I cannot invalidate in this case my timer...but also I cannot use NSTimer declared as member of class in static method...How to be ? thanks....
Just prefix the variable with static.
static NSTimer *timer;
If you overwrite the timer variable, you should invalidate it first, because otherwise you'll have a timer running, that you don't have access to anymore.
I think you should be able to write this without static functions but you can also do this using static functions. If there should be only one instance of that class then use a singleton-pattern.
for doing this with static functions then just use
static NSTimer *timer
but you also have to retain your new and also release the old timer because the function scheduledTimer... allocs an NSTimerinstance but also autoreleases it. If dont retain that timer then the timer will be deallocated later and will afaik not call your method.

check for time duration during loop

I am using MBProgressHUD to display a progress indicator. The delegate that is called when the indicator is shown is:
- (void)myTask {
while (self.show_progress == NO){
}
}
basically when it goes out of the loop it dismisses the indicator. Now the issue is that I would like do something more in this method. I would like to check for how long has the indicator been spinning for, if it has been more than 5 seconds then I would like to re-load the request. The question is how do I check for this?
This is just to prevent the apps waiting for an infinite amount of time just in case the response never got back or got stuck somewhere.
I'm not familiar with MBProgressHUD , but on general terms you could do the following:
When you first make the request do:
NSDate *startTime = [NSDate date];
Then whenever you want to check how long has it been:
NSTimeInterval timePassed = -[startTime timeIntervalSinceNow];
timePassed will have the value, in seconds, of how long has it been since you made your request. May be you should consider using NSTimer for this: Schedule a timer that will fire 5 seconds after you performed your request, if it triggers cancel the request but if you receive a response before the timer triggers invalidate the timer.
If I understand correctly, your code just waits until the property show_progress becomes NO. I don't know why your code does this, it seems a little inelegant. If you want to keep it this way, at least use a condition lock to prevent the 100% CPU usage:
Prepare the condition lock like this:
NSConditionLock *progressLock = [[NSConditionLock alloc] initWithCondition:0];
In your second thread, once your loading stuff or whatever finishes, change the condition like this:
[progressLock lock];
[progressLock unlockWithCondition:1];
And in your method, do this:
- (void)myTask {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:5];
if ([progressLock lockWhenCondition: 1 beforeDate:timeoutDate) {
// we aquired the lock, processing has finished
[progressLock unlock];
} else {
// we didn't aquire the lock because the 5 seconds have passed
// reload the request or do whatever you want to do
}
}
This method waits 5 seconds, and then times out. It uses no CPU in those 5 seconds, because it waits for a signal at the lockWhenCondition:beforeDate: call.
The way I've gone about similar situations is to set up a timer. The basic concept would be to start the timer when the indicator starts spinning. Then invalidate it when the indicator stops. Else if it goes on for 5 seconds, execute your method.
So in your header, you'll want
NSTimer *myTimer;
then in the implementation, when you start the indicator spinning,
[indicator startAnimating];
if (myTimer != nil) {
[myTimer invalidate];
myTimer = nil;
}
myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(reloadRequest) serInfo:nil repeats:NO];
when you stop the indicator from spinning, send [myTimer invalidate]; and myTimer = nil;. If the specified time is reached beforehand, reload the request in your reloadRequest method

Using NSTimer in a Loop

I need to show a timer counting down from 30 to 0, several times. (30 to 0, start over at 30, etc) However when I placed it in a for-loop, instead of waiting till the timer invalidates to begin the next timer, the loop iterates through and several timers are created.
How can I form a loop that waits until the timer has invalidated?
- (IBAction)startCountdown:(id)sender {
NSNumber *rounds = [[NSNumber alloc]initWithInt:sliderRounds.value];
for (int i = rounds.intValue; i > 0; i--) {
[self timer];
}
You can use the scheduledTimerWithTimeInterval
[NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: NO];
And inside the handleTimer you create the next timer
Looks like you are calling some method called 'timer' on self. What does it do? Creates a new timer? Then this is why. When a timer is created, it doesn't block. Instead a method call is scheduled and executed once the timer timeout passes.
What you could do is the following:
Create a class that holds an NSTimer
This class would have a variable to hold how many times it needs to count
At the end of one timer (and really, the only one) firing, it decrements the count
You can have the NSTimer repeat by calling it with:
[NSTimer scheduledTimerWithTimeInterval:30.0f
target:self
selector:#selector(timerFired)
userInfo:nil
repeats:YES];
In a method called - (void)timerFired, you would decrement the count. If it reaches zero, you would stop the timer.
Hope this helps!

Loop Until Condition Reached, iPhone

I have a problem here...
After a button is pressed I want a loop to run until a condition is reached:
- (IBAction)buttonclick1 ...
if ((value2ForIf - valueForIf) >= 3) { ...
I would like a loop to run until
((value2ForIf - valueForIf) >= 3)
and then execute the code associated with the IF statement.
What I am aiming to achieve is the program to keep checking if the above statement is true, before continuing with the code. On top of this there is an else statement beneath the IF, though I don't know if this will affect a loop.
I am unsure of the format of the loop required here and everything I have tried has caused errors. Any help would be greatly appreciated.
Stu
- (IBAction)buttonclick1 ...
{
//You may also want to consider adding a visual cue that work is being done if it might
//take a while until the condition that you're testing becomes valid.
//If so, uncomment and implement the following:
/*
//Adds a progress view, note that it must be declared outside this method, to be able to
//access it later, in order for it to be removed
progView = [[MyProgressView alloc] initWithFrame: CGRectMake(...)];
[self.view addSubview: progView];
[progView release];
//Disables the button to prevent further touches until the condition is met,
//and makes it a bit transparent, to visually indicate its disabled state
thisButton.enabled = NO;
thisButton.alpha = 0.5;
*/
//Starts a timer to perform the verification
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: 0.2
target: self
selector: #selector(buttonAction:)
userInfo: nil
repeats: YES];
}
- (void)buttonAction: (NSTimer *) timer
{
if ((value2ForIf - valueForIf) >= 3)
{
//If the condition is met, the timer is invalidated, in order not to fire again
[timer invalidate];
//If you considered adding a visual cue, now it's time to remove it
/*
//Remove the progress view
[progView removeFromSuperview];
//Enable the button and make it opaque, to signal that
//it's again ready to be touched
thisButton.enabled = YES;
thisButton.alpha = 1.0;
*/
//The rest of your code here:
}
}
Rather than run a tight loop, which would block your app's execution unless run on another thread, you could use an NSTimer to call a method at a time interval of your choosing and check the condition in that method. If the condition is satisfied, you can invalidate the timer and continue.
From what you stated, what you want is a while loop
while( (value2ForIf - valueForIf) < 3 ) { ...Code Here... }
This will run the code in the braces as long as the difference in values is less than 3, meaning it will run until their difference is 3 or greater. But as Jasarien said. This is a bad idea since you will be blocking your program. If the values are being updated by the code itself that is fine. But if they are being updated by some UI from the user, your while loop will block the UI and not allow the user to input anything.