Question

I'm using an NSTimer, and in certain circumstances I want to double how long before the method NSTimer calls gets called.

Basically: if I have it set to call every 0.5 seconds, once in awhile I want to delay it so it waits 1.0 seconds (double the time) before the next time it's called.

I'm having a great deal of difficulty implementing this, however. This was my initial attempt:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [NSTimer scheduledTimerWithTimeInterval:0.5 
                                     target:self 
                                   selector:@selector(timerMethod:) 
                                   userInfo:nil 
                                    repeats:YES];
}

- (void)timerMethod:(NSTimer *)timer {
    static int variable = 1;

    switch (variable) {
        case 1:
            self.stopLightLabel.textColor = [UIColor redColor];
            break;
        case 2:
            self.stopLightLabel.textColor = [UIColor orangeColor];
            break;
        case 3:
            self.stopLightLabel.textColor = [UIColor yellowColor];
            break;
        case 4: // Stop for twice as long here
            self.stopLightLabel.textColor = [UIColor greenColor];

            NSTimeInterval timeBetweenNowAndFireDate = [timer.fireDate timeIntervalSinceNow];

            // Create a new date that is twice as far in the future in order to give a long pause
            timer.fireDate = [NSDate dateWithTimeIntervalSinceNow:(timeBetweenNowAndFireDate * 2)];

            break;
        case 5:
            self.stopLightLabel.textColor = [UIColor blueColor];
            break;
        case 6:
            self.stopLightLabel.textColor = [UIColor purpleColor];
            break;
        default:
            break;
    }

    variable++;
}

I try to get the amount of time between right now and when the timer will fire. In the example above this should be 0.5. I then set the fireDate to a new value, which is twice the previous.

This does not work, however.

Without that conditional, the timer works normally but obviously does not delay under a certain circumstance (for that circumstance it gets the same amount of time as everything else).

With the conditional, it appears shorter even! In fact, it doesn't get longer until I multiply it by around 400 instead of 2!

What am I doing wrong here?

Était-ce utile?

La solution

you call the code to modify the fireDate inside the timerFired method. That's too early. The fireDate is the date when the timer DID fire there.. it isnt updated yet.

one easy fix: wrap it in a dispatch async. that lets the timer return and update its fireDate BEFORE you try to modify it:

fixed code:

- (void)timerMethod:(NSTimer *)timer {
    static int variable = 1;

    dispatch_async(dispatch_get_main_queue(), ^{
        [self afterTimerFired:timer];
    }
 }

- (void)afterTimerFired:(NSTimer *)timer {
    static int variable = 1;        
        switch (variable) {

            case 4: // Stop for twice as long here
                self.stopLightLabel.textColor = [UIColor greenColor];
                ...
                NSTimeInterval timeBetweenNowAndFireDate = [timer.fireDate timeIntervalSinceNow];
                NSLog(@"%f", timeBetweenNowAndFireDate);
                // Create a new date that is twice as far in the future in order to give a long pause
                timer.fireDate = [NSDate dateWithTimeIntervalSinceNow:(timeBetweenNowAndFireDate * 2)];
                ...

OR

dont dispatch async and make fireDate = [NSDate dateWithTimeIntervalSinceNow:fire.timeInterval*2] wich might be even cleaner if possible

Autres conseils

I'm fairly certain that you can't change the fireDate of a running timer. (This is wrong. See the edit below.)

The other poster, Paul.s, had the same idea I did. If you have a timer that fires every .5 seconds, add a BOOL skipThisInterval instance variable to the object that is the timer's target.

Then, in your timer method, if skipThisInterval is true, simply reset skipThisinterval = FALSE and return without doing anything else. That way the timer will fire again after another interval has passed and then continue running as before.

Failing that, you'll have to invalidate the running timer, create a new timer that will fire once at your desired end time, and then in the method that timer invokes, create a new repeating timer that goes back to firing on the old interval.

EDIT: I was wrong. You can change the fire date of an "in-flight" timer. (Thanks for Josh Caswell for correcting me on that.)

Even still, if you just want your timer to simply skip it's next firing and keep going, simply adding logic to the method the timer invokes to skip the next firing seems simpler and cleaner than adjusting the fireDate (and the docs say that changing the fire date is relatively expensive.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top