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
Startupdatelocations in Background, Didupdatingtolocation Only Called 10-20 Times
How to Build a Url with Query Parameters Containing Multiple Values for the Same Key in Swift
Uicollectionview Update a Single Cell
How to Both Stroke and Fill with Nsattributedstring W/ Uilabel
What Is the Correct Way to Use Prepareforreuse
How to Add a Image in Email Body Using Mfmailcomposeviewcontroller
How to Access Files in Icloud Drive from Within My iOS App
Initializer Does Not Override a Designated Initializer from Its Superclass
Determine If iOS Device Is 32- or 64-Bit
Detecting Touches on Mkoverlay in iOS7 (Mkoverlayrenderer)
How to Make Reorder Control of Uitableviewcell in Left Side
-[Nsnull Length]: Unrecognized Selector Sent to JSON Objects
How to Programmatically Add a Proxy to an Nsurlsession
What's the Equivalent of the Uisplitviewcontroller in Swiftui
Swift - Image Data from Ciimage Qr Code/How to Render Cifilter Output
Wkwebview Page Height Issue on iPhone X