Search code examples
objective-ccocoa

Countdown Timer


I'm trying to create a countdown timer that takes countdown, an IBOutlet connected to a textfield, from 60 seconds down to 0. I'm not sure

A. How to limit the repeats to 60 and

B. How to decrement the countdown in advanceTimer:

- (IBAction)startCountdown:(id)sender
{
    NSTimer *countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self     selector:@selector(advanceTimer:) userInfo:nil repeats:YES];
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:countdownTimer forMode:NSDefaultRunLoopMode];
}

- (void)advanceTimer:(NSTimer *)timer
{
    [countdown setIntegerValue:59];
}

Solution

  • You're on the right track so far.

    Sticking with the code you already have, here is how advanceTimer method should look to make it work:

    - (void)advanceTimer:(NSTimer *)timer
    {
        [countdown setIntegerValue:([countdown integerValue] - 1)];
        if ([countdown integerValue] == 0)
        {
            // code to stop the timer
        }
    }
    

    edit: To make the whole thing more object-oriented, and to avoid converting from strings to numbers and back every time, I would instead do something like this:

    // Controller.h:
    @interface Controller
    {
        int counter;
        IBOutlet NSTextField * countdownField;
    }
    @property (assign) int counter;
    - (IBAction)startCountdown:(id)sender;
    @end
    

    // Controller.m:
    @implementation Controller
    
    - (IBAction)startCountdown:(id)sender
    {
        counter = 60;
    
        NSTimer *countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1
                                             target:self
                                           selector:@selector(advanceTimer:)
                                           userInfo:nil
                                            repeats:YES];
    }
    
    - (void)advanceTimer:(NSTimer *)timer
    {
        [self setCounter:(counter -1)];
        [countdownField setIntegerValue:counter];
        if (counter <= 0) { [timer invalidate]; }
    }
    
    @end
    

    And, if you can make use of bindings, you could simply bind the text field's intValue to the counter property of the Controller. This would allow you to elminate the IBOutlet in the class interface, and the setIntegerValue: line in advanceTimer.

    update: Removed the code which adds the timer to the run loop twice. Thank you to Nikolai Ruhe and nschmidt for noticing that error.

    update: Used the setIntegerValue method to simplify the code, as per nschmidt.

    edit: Typo in definition of (void)advanceTimer:(NSTimer *)timer ... caused annoying 'unrecognized selector sent to instance' exception