How to Program a Delay in Swift 3

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.

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 can I call a function after a time delay in the main thread in Swift 3?

I don't think it needs to be anywhere near that complicated. You can just use a Timer;

class MyClass: UIViewController {

var tapCount = 0
var tapTimer: Timer?

@IBAction tapped(_ sender: Any) {
if tapCount < 3 {
tapCount += 1
tapTimer?.invalidate()
tapTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { (timer) in
switch (self.tapCount) {
case 1:
self.f1()
case 2:
self.f2()
case 3:
self.f3()
default:
// Hmm, shouldn't happen
}
self.tapCount = 0
})
}
}

The timer will be scheduled on the main queue by default, so there is no need to dispatch anything on the main queue specifically

Adding delay in swift 3 in Playground appears inaccurate

let key = readLine()!

blocked the main thread until you do some input. .asyncAfter will dispatch your code in the same thread, but the code is not able to run until readLine finished.

UPDATE

try in the Playground

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

import Dispatch
import Foundation

let t = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)
t.scheduleRepeating(deadline: .now(), interval: 3.0)
var i = 10
t.setEventHandler {
print(Date())
i -= 1
if i < 0 {
PlaygroundPage.current.finishExecution()
}
}
t.resume()

something much closer to you needs :-)

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

import Dispatch
import Foundation

let delay_array = [1.0, 5.9, 10.2, 22.1, 31.5, 40.9, 51.8, 61.0]
var tarr:[DispatchSourceTimer] = []
let start = Date()
let dt: DispatchTime = .now()

for delay in delay_array {
let t = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)
t.scheduleOneshot(deadline: dt + delay)
t.setEventHandler {
print(start.timeIntervalSinceNow)
}
t.resume()
tarr.append(t)
}

There is not necessary to use an array of dispatch sources, you can reuse the source ... it is up to you. Look at the precision :-), it is great, isn't it?

reusing the same source, you can write something like

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

import Dispatch
import Foundation

let delay_array = [1.0, 5.9, 10.2, 22.1, 31.5, 40.9, 51.8, 61.0]
let start = Date()

let dt: DispatchTime = .now()
let t = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)

var i = 0
t.scheduleOneshot(deadline: dt + delay_array[i])

t.setEventHandler {
print(start.timeIntervalSinceNow)
t.suspend()
i += 1
if i < delay_array.count {
t.scheduleOneshot(deadline: dt + delay_array[i])
t.resume()
} else {
t.cancel()
PlaygroundPage.current.finishExecution()
}
}
t.resume()

Swiftui NavigationView timer delay

This should work:

struct MyView: View {
@State var pushNewView: Bool = false

var body: some View {
NavigationView {
NavigationLink(isActive: $pushNewView) {
Text("Second View")
} label: {
Text("First View") //Replace with some animation content
}
}
.onAppear {
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
pushNewView = true
}
}
}
}

Delay Actions in Swift

dispatch_after() is the standard way of delaying actions.

indicator.startAnimating()

let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
indicator.stopAnimating()
}

See: dispatch_after - GCD in swift?


Update for Swift 3.0

indicator.startAnimating()

let delay = Int(4.5 * Double(1000))
DispatchQueue.main.after(when: .now() + .milliseconds(delay)) {
indicator.stopAnimating()
}

However, in the spirit of Swift 3.0, I think extending DispatchQueue would be a better solution.

extension DispatchQueue {
func delay(_ timeInterval: TimeInterval, execute work: () -> Void) {
let milliseconds = Int(timeInterval * Double(1000))
after(when: .now() + .milliseconds(milliseconds), execute: work)
}
}

This leaves us with a very nice

indicator.startAnimating()

DispatchQueue.main.delay(4.5) {
indicator.stopAnimating()
}

Update 2

Digging into the Xcode 8.0 beta, I found public func +(time: DispatchTime, seconds: Double) -> DispatchTime. So, I guess this is valid…

indicator.startAnimating()

DispatchQueue.main.after(when: .now() + 4.5) {
indicator.stopAnimating()
}

I don't think there is a need to extend DispatchQueue for something this clean already.

--

Update for Swift 3.1

There is new syntax for Swift 3.1. They just likes to change things don't they.

indicator.startAnimating()

DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
indicator.stopAnimating()
}

Is there a way to delay a return-Statement in Swift?

You are looking for a closure, a.k.a. completion handler.

return executes immediately and there is no way to delay that. Instead, you can use completion handlers, which work by passing in a closure as a parameter. This closure can then be called after a delay.

                            /// closure here!
func returnLate(completion: @escaping ((String) -> Void)) {
var string = "Wrong"
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
string = "right"
completion(string) /// similar to `return string`
}
}

override func viewDidLoad() {
super.viewDidLoad()
returnLate { string in
print("String is \(string)") /// String is right
}
}


Related Topics



Leave a reply



Submit