Nstimer Not Fired When Uiscrollview Event Occurs

NSTimer not fired when uiscrollview event occurs

iOS Applications run on an NSRunLoop. Each NSRunLoop has different modes of execution for different tasks. For example, the default nstimer is scheduled to run under the NSDefaultRunMode on the NSRunLoop. What this means however is that certain UIEvents, scrollviewing being one, will interrupt the timer, and place it on a queue to be run as soon as the event stops updating. In your case, in order to get the timer to not be interrupted, you need to schedule it for a different mode, namely NSRunLoopCommonModes, like so:

  self.myTimer =  [NSTimer scheduledTimerWithTimeInterval:280
target:self
selector:@selector(doStuff)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSRunLoopCommonModes];

This mode will allow your timer to not be interrupted by scrolling.
You can find more about this info here: https://developer.apple.com/documentation/foundation/nsrunloop
At the bottom you will see the definitions of the modes you can choose from. Also, legend has it, you can write your own custom modes, but few have ever lived to tell the tale im afraid.

UIScrollView pauses NSTimer while scrolling

An easy way to fix this is adding your NSTimer to the mainRunLoop.

[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

To remove a timer from all run loop modes on which it is installed, send an invalidate message to the timer.

UIScrollView pauses NSTimer until scrolling finishes

An easy & simple to implement solution is to do:

NSTimer *timer = [NSTimer timerWithTimeInterval:... 
target:...
selector:....
userInfo:...
repeats:...];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

UISlider doesn't animate while it's superview is scrolling

By default, NSTimers won't fire when a scroll view is scrolling. You have to add the timer to the mainRunLoop for NSRunLoopCommonModes:

    [[NSRunLoop mainRunLoop] addTimer:yourTimer forMode:NSRunLoopCommonModes];

Event tracking in UIScrollView blocks the main thread. Any fixes?

Instead of using one of NSTimer's static constructors, create the timer object and schedule it manually for the "common" run loop modes:

Swift

let timer = Timer.init(timeInterval: 10, repeats: false) { (_) in
// ... do something useful after 10 seconds ...
}
RunLoop.main.add(timer, forMode: .commonModes)

Objective C

NSTimer *timer = [[NSTimer alloc] initWithFireDate: [NSDate dateWithTimeIntervalSinceNow: delayInSeconds] interval: 0 target:yourObject selector:yourSelector userInfo:nil repeats:NO];
[NSRunLoop.mainRunLoop addTimer: timer forMode: NSRunLoopCommonModes];

Your paramters when constructing the timer will be different since you're using a repeating timer, but the basic idea is to manually schedule the timer for NSRunLoopCommonModes.

What's happening is that when you're scrolling the run loop goes into a mode which prevents "default mode" tasks from executing. The static NSTimer functions all schedule into the default mode which is why they don't fire while you're scrolling. Manually scheduling the timer lets you specify the mode and NSRunLoopCommonModes includes the run mode used by scrolling.

UIScrollView blocks run loop?

The reason that the timer stops firing is that the run loop switches to UITrackingRunLoopMode during scrolling and the timer is not added by default to that mode. You can do that manually when you start the timer:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];

Call an action at specific intervals in iOS

Without much code its hard to tell what you issues is, but here are some things to try apologies if any of this is obvious.


Are you holding onto a reference to the timer?

This might be useful for debugging. If you have a property called makeTargetTimer, then you could do this:

NSTimer * makeTargetTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(createTargets) userInfo:nil repeats:YES];
self.makeTargetTimer = makeTargetTimer // Save to a property for later use (or just use an iVar)

The only way to stop a re-occurring timer is to invalidate it. Therefore you could check to see if its been invalidated.

BOOL isInvalidated = [self.makeTargetTimer isValid];

Also you might want to do this in your dealloc method anyway:

- (void) dealloc {
[_makeTargetTimer invalidate]; // Stops the timer from firing (Assumes ARC)
}

Are you scrolling when the even should be received?

If you want the timer to be fired while scrolling then you need to use NSRunLoopCommonModes. There is a excellent expiation in this question.

 [[NSRunLoop currentRunLoop] addTimer:makeTargetTimer forMode:NSRunLoopCommonModes]; 

What is your implementation of createTargets like?

  • Have you put NSLog statements on the body of this method. Are you certain its not being called?

My custom UI elements are not being updated while UIScrollView is scrolled

While scrolling, the main thread's run loop is in UITrackingRunLoopMode. So what you need to do is schedule your timer in that mode (possibly in addition to the default mode). I believe NSRunLoopCommonModes includes both the default and event tracking modes, so you can just do this:

NSTimer *timer = [NSTimer timerWithTimeInterval:0.42 target:foo selector:@selector(doSomething) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

By the way, you should do this all on the main thread. No need to spawn a background thread (unless each timer invocation is going to do some lengthy processing).

Timer that updates label running in background

First of all keep in mind that you cannot perform UI changes in any other thread than the main thread. Having said that, you need the NSTimer to fire in the main queue, otherwise the program will crash when changing the UILabel. Have a look at this links http://bynomial.com/blog/?p=67 and this one http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSRunLoop_Class/Reference/Reference.html

To my knowledge, if you schedule the timer in the for the NSRunLoopCommonModes it will ignore event updates and fire the timer jsut how you want it:

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

-(void) timerDidTick:(NSTimer*) theTimer{
[[self myLabel] setText:@"Timer ticked!"];
}


Related Topics



Leave a reply



Submit