NSTimer - how to delay in Swift
Swift 3
With GCD:
let delayInSeconds = 4.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
// here code perfomed with delay
}
or with a timer:
func myPerformeCode() {
// here code to perform
}
let myTimer : Timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(self.myPerformeCode), userInfo: nil, repeats: false)
Swift 2
With GCD:
let seconds = 4.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
// here code perfomed with delay
})
or with a timer:
func myPerformeCode(timer : NSTimer) {
// here code to perform
}
let myTimer : NSTimer = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("myPerformeCode:"), userInfo: nil, repeats: false)
How to program a delay in Swift 3
After a lot of research, I finally figured this one out.
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { // Change `2.0` to the desired number of seconds.
// Code you want to be delayed
}
This creates the desired "wait" effect in Swift 3 and Swift 4.
Inspired by a part of this answer.
Delay timer after first fire
First schedule the timer as you've done.
timer = NSTimer.scheduledTimerWithTimeInterval(
10.0, target: self,
selector: "searchForDrivers:",
userInfo: nil,
repeats: true
)
Then, immediately afterwards, fire timer
.
timer.fire()
According to the documentation,
You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.
See the NSTimer Class Reference for more information.
How to create a delay in Swift?
Instead of a sleep, which will lock up your program if called from the UI thread, consider using NSTimer
or a dispatch timer.
But, if you really need a delay in the current thread:
do {
sleep(4)
}
This uses the sleep
function from UNIX.
How Do I Cancel and Restart a Timed Event in Swift?
Use a timer:
weak var clearTimer: Timer?
And:
override func viewDidLoad() {
super.viewDidLoad()
startClearTimer()
}
func startClearTimer() {
clearTimer = Timer.scheduledTimer(
timeInterval: 3.0,
target: self,
selector: #selector(clearLabel(_:)),
userInfo: nil,
repeats: false)
}
func clearLabel(_ timer: Timer) {
label.text = ""
}
func volumeSliderValueChange(sender: UISlider) {
clearTimer?.invalidate() //Kill the timer
//do whatever you need to do with the slider value
startClearTimer() //Start a new timer
}
Sleep or delay a Timer thread in Swift
NSTimer
is not that accurate. Its maximum resolution is somewhere around 50 - 100ms. Anyhow, you can add a variable to control the firing of the timer:
var doNotUpdate = false
if distance <= respawnDistance * 0.1 {
doNotUpdate = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
doNotUpdate = false
}
}
func updatePosition() {
if doNotUpdate { return }
// update your position
}
NSTimer Without Initial Delay
You can't make a timer fire before its time interval has elapsed, but you can do this:
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(updateTime) userInfo:nil repeats:YES];
[self updateTime];
Swift: NSTimer, delay hiding UIButton with each tap
Is the timer still valid while it calls the method in the selector?
Yes, it is
The following is a complete runnable playground example demonstrating that:
import Foundation
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()
class Bla {
var timer : NSTimer?
init() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "check", userInfo: nil, repeats: false)
NSTimer.scheduledTimerWithTimeInterval(1.1, target: self, selector: "checkAgain", userInfo: nil, repeats: false)
}
@objc func check(){
print(timer!.valid)
}
@objc func checkAgain(){
check()
}
}
let bla = Bla()
This will first output true
and then output false
. The timer is still valid when firing. As soon as you leave the fired method the timer gets invalidated.
That will render your if
useless since the method will do nothing when fired by the timer because anything && !true
evaluates to false
.
What you can do to circumvent that issue is create a different method timerFired
which sets some internal property of your class, e.g. var timerHasFired = false
to true
. Your `` then has to check that variable instead of the timer:
var timer = NSTimer()
var timerHasFired = false
func hideAndShrink(){
if !self.hidden && hideAndShrink {
self.hidden = true
}
}
func timerFired() {
timerHasFired = true
hideAndShrink()
}
func justTapped(){
timer.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "timerFired", userInfo: nil, repeats: false)
}
NSTimer Too Slow
There is a whole mess of problems here. Let's clean this up.
class ViewController: UIViewController {
@IBOutlet var timerLabel: UILabel!
First of all, don't use a timer to update the label. Use a CADisplayLink
. A display link synchronizes with the screen refresh interval, which is 1/60 of a second on most iOS devices (not 1/100), so you don't do extra work:
private var displayLink: CADisplayLink?
Next, don't try to track the elapsed time by incrementing a counter when the timer (or link) fires, because the timer or link is not guaranteed to fire as often as you requested. Instead, store the time at which the timer was started, and the time at which it was stopped (if it was stopped):
private var startTime: CFAbsoluteTime = 0
private var endTime: CFAbsoluteTime = 0 {
didSet {
updateLabel()
}
}
Track the state using an enum
instead of mysterious hard-coded numbers. And since the label color depends only the state, add a property to the state that gives the label color for that state:
private enum State {
case Stopped
case Pending
case Running
var labelColor: UIColor {
switch self {
case .Pending: return UIColor.greenColor()
case .Stopped, .Running: return UIColor.blackColor()
}
}
}
The elapsed time depends on the state, so add a method to compute it:
private var elapsedTime: NSTimeInterval {
switch state {
case .Stopped: return endTime - startTime
case .Running: return CFAbsoluteTimeGetCurrent() - startTime
case .Pending: return 0
}
}
Use a format string to convert the elapsed time to a string when updating the label:
private func updateLabel() {
timerLabel.text = String(format: "%.02f", elapsedTime)
}
Changing the timer state can change both the label color and the elapsed time, so update the label color and the label text when the state changes:
private var state = State.Stopped {
didSet {
timerLabel.textColor = state.labelColor
updateLabel()
}
}
When a touch begins, create the display link if needed, then update the state. The state's didSet
will handle updating the label as necessary:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
createDisplayLinkIfNeeded()
switch state {
case .Stopped:
state = .Pending
case .Pending:
break
case .Running:
state = .Stopped
endTime = CFAbsoluteTimeGetCurrent()
displayLink?.paused = true
}
}
When a touch ends, start the timer if necessary:
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if state == .Pending {
startTime = CFAbsoluteTimeGetCurrent()
displayLink?.paused = false
state = .Running
}
}
Here's how you create the display link:
private func createDisplayLinkIfNeeded() {
guard self.displayLink == nil else { return }
let displayLink = CADisplayLink(target: self, selector: "displayLinkDidFire:")
displayLink.paused = true
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
self.displayLink = displayLink
}
And here's the method the display link will call:
func displayLinkDidFire(_: CADisplayLink) {
updateLabel()
}
} // end of ViewController
Related Topics
Error Appstore Connect:Missing Purpose String in Info.Plist File
Show Search Bar in Navigation Bar Without Scrolling on iOS 11
Uicollectionview Scrolling Choppy When Loading Cells
"Interfaceorientation" Is Deprecated in iOS 8, How to Change This Method Objective C
Upload Multiple Images in Swift Using Alamofire
Text Color Based on Background Image
Swift - Uiimagepickercontroller - How to Use It
Showing a Uipickerview with Uiactionsheet in iOS8 Not Working
How to Find Indexpath for Tapped Button in Tableview Using Seque
The Identity Used to Sign the Executable Is No Longer Valid
How to Get Selected Value from Uipickerview
How to Check If a Framework Is Bitcode Supported for Xcode7
Repeating Local Notifications for Specific Days of Week (Swift 3 iOS 10)
Implementing Auto Layout for Views Generated Programmatically
Are Afnetworking Success/Failure Blocks Invoked on the Main Thread
Xcode 4.3: Codesign Operation Failed (Check That the Identity You Selected Is Valid)