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
How to Display the Default iOS 6 Share Action Sheet with Available Share Options
Ask for User Permission to Receive Uilocalnotifications in iOS 8
How to Draw Radial Gradients in a Calayer
Xcode 4: Build Failed, No Issues
Add Left/Right Horizontal Padding to Uilabel
Notificationcenter Issue on Swift 3
How to Draw Border Around a Uilabel
Xcode 8/Swift 3: "Expression of Type Uiviewcontroller? Is Unused" Warning
Handling Touch Event in Uilabel and Hooking It Up to an Ibaction
How to Get the Height and Width of an Uiimage
How to Implement a Swiping/Sliding Animation Between Views
Using Existing System Sounds in iOS App [Swift|
Add Uipickerview in Uiactionsheet from iOS 8 Not Working
How to Display Clickable Links in Uitextview
How to Set the Uinavigationbar with Gradient Color
Didreceiveremotenotification: Fetchcompletionhandler: Open from Icon VS Push Notification