18

I have an application with a countdown timer. I've made it with a label that is updated with a function called from a timer this way:

...
int timeCount = 300; // Time in seconds
...
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(actualizarTiempo:) userInfo:nil repeats:YES];
...
- (void)actualizaTiempo:(NSTimer *)timer {
    timeCount -= 1;
    if (timeCount <= 0) {
        [timer invalidate];
    } else {
        [labelTime setText:[self formatTime:timeCount]];
    }
}

Note: formatTime is a function which receive an integer (number of seconds) and returns a NSString with format mm:ss

Everything works ok, that is, the time counts down but the problem is that I have a UITableView in the application and if I touch the table and drag it (to move along the cells) the timer stops until I release my finger from the screen...

Is this behavior normal? If it is, is there any way to avoid it and make the timer work while dragging the table?

rai212
  • 711
  • 1
  • 6
  • 20

4 Answers4

34

By using scheduledTimerWithTimeInterval:, as j.tom.schroeder says, your timer is automatically schedule on the main run loop for the default modes. This will prevent your timer from firing when your run loop is in a non-default mode, e.g., when tapping or swiping.

The solution, though, is not using a thread, but scheduling your timer for all common modes:

NSTimer *timer = [NSTimer timerWithTimeInterval:1
                                         target:self
                                       selector:@selector(actualizarTiempo:)
                                       userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

Depending on the kind of events you would like to allow without them stopping your timers, you might also consider UITrackingRunLoopMode. For more info about run loop modes, see Apple Docs.

sergio
  • 68,819
  • 11
  • 102
  • 123
  • Thank you very much for your help, it works with this solution. I'll take a closer look to Loop Modes to get the one that fits better to my events – rai212 Jul 30 '12 at 14:54
  • 1
    don't forget to `[timer invalidate];` on `viewWillDisappear` or any other method or you will fire the timer again and again. – skornos Jul 21 '15 at 10:26
5

Heres the Swift version:

Swift 2

var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "removeFromSuperview", userInfo: nil, repeats: false)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)

Swift 3, 4, 5

var timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(removeFromSuperview), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
Karen Hovhannisyan
  • 1,140
  • 2
  • 21
  • 31
Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
1

Swift version 4+:

var timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.TimerController), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)

if you want to repeat for example as a counter then set repeat = true else = false

iman kazemayni
  • 1,255
  • 1
  • 19
  • 20
0

This is normal behavior. scheduledTimerWithTimeInterval: schedules on the current run loop, so any interaction with the UITableView will block the actualizarTiempo: method from being called. Use the NSThread methods detachNewThreadSelector:toTarget:withObject and sleepForTimeInterval to perform actualizarTiempo: on a separate thread.

jtomschroeder
  • 1,034
  • 7
  • 11
  • Really the problem was related to the fact that the timer was related to the current run loop. Using NSRunLoop with different loop mode fixes the problem. Thank you – rai212 Jul 30 '12 at 14:57