(Swift) NSTimer Stop when Scrolling
I created a simple project with a scrollView and a label that is updated with an NSTimer
. When creating the timer with scheduledTimerWithInterval
, the timer does not run when scrolling.
The solution is to create the timer with NSTimer:timeInterval:target:selector:userInfo:repeats
and then call addTimer
on NSRunLoop.mainRunLoop()
with mode
NSRunLoopCommonModes
. This allows the timer to update while scrolling.
Here it is running:
Here is my demo code:
class ViewController: UIViewController {
@IBOutlet weak var timerLabel: UILabel!
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
timerLabel.text = "0"
// This doesn't work when scrolling
// let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update", userInfo: nil, repeats: true)
// Do these two lines instead:
let timer = NSTimer(timeInterval: 1, target: self, selector: "update", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
}
func update() {
count += 1
timerLabel.text = "\(count)"
}
}
Swift 3:
let timer = Timer(timeInterval: 1, target: self, selector: #selector(update), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
Swift 4, 5:
let timer = Timer(timeInterval: 1, target: self, selector: #selector(update), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
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];
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.
Weird Bug or Issue using Timer and ScrollView together
From The ultimate guide to Timer:
Working with runloops
One common problem folks hit when using timers is that they won’t fire
when the user is interacting with your app. For example, if the user
has their finger touching the screen so they can scroll through a
table view, your regular timers won’t get fired.This happens because we’re implicitly creating our timer on the
defaultRunLoopMode, which is effectively the main thread of our
application. This will then get paused while the user is actively
interacting with our UI, then reactivated when they stop.The easiest solution is to create the timer without scheduling it
directly, then add it by hand to a runloop of your choosing. In this
case, .common is the one we want: it allows our timers to fire even
when the UI is being used.
A solution is to run your timer on the RunLoop
in the common
mode:
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerFunction), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: .common) // add this line
}
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.
Why timer stops when scrolling in UIWebView iPhone?
You have to add the timer to another RunLoopMode. Per default a timer is added to the NSDefaultRunLoopMode. That means that the timer is only executing when the app’s run loop is in NSDefaultRunLoopMode.
Now, when a user touches the screen (e.g. to scroll a UIScrollView) the run loop’s mode will be switched to NSEventTrackingRunLoopMode. And now, that the run loop is not in NSDefaultRunMode anymore, the timer will not execute. The ugly effect of that is, that timer is blocked whenever the user touches the screen. And that can be a looong time when the user is scrolling, because the timer is blocked until the scrolling completely stops. And when the user continues to scroll, the timer is blocked again.
Fortunately the solution to this problem is quite simple: You can add your timer to another NSRunLoopMode. When add the timer to NSRunLoopCommonModes it will execute in all run loop modes (that have been declared as a member of the set of “common” modes, to be precise). That means that the timer is not only working in NSDefaultRunLoopMode but also in NSEventTrackingRunLoopMode (when the user touches the screen).
So after you initialize your timer, add it to NSRunLoopCommonModes:
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Related Topics
Fbsdkapplicationdelegate Application Openurl:Sourceapplication:Annotation Deprecated
Binary Operator '===' Cannot Be Applied to Operands of Type 'Any' and 'Uibarbuttonitem!'
Is It Possible in Swift to Add Variables to an Object at Runtime
iOS Tabbar Item Title Issue in iOS13
What Is the Role of Avcapturedevicetype.Builtindualcamera
Scenekit - Animation with Dae File Format
Read a File in a MACos Command Line Tool Project
Why Does Swift Return an Unexpected Pointer When Converting an Optional String into an Unsafepointer
How to Encode Realm's List<> Type
Show Folder's Contents in Finder Using Swift
Sub-Classing Nstextstorage Causes Significant Memory Issues
Reversing the Order of a String Value
Need to Check That Braces in Given Array Are Balanced or Not
Deploy App with Pre-Populated Core Data
How to Open Another Window in MACos in Swift with Cocoa
Swift Package Manager - Type 'Bundle' Has No Member "Module" Error