I want to pause my app at a certain in point. In other words, I want my app to execute the code, but then at a certain point, pause for 4 seconds, and then continue on with the rest of the code. How can I do this?
I am using Swift.
Using a dispatch_after block is in most cases better than using sleep(time) as the thread on which the sleep is performed is blocked from doing other work. when using dispatch_after the thread which is worked on does not get blocked so it can do other work in the meantime. If you are working on the main thread of your application, using sleep(time) is bad for the user experience of your app as the UI is unresponsive during that time.
Dispatch after schedules the execution of a block of code instead of freezing the thread:
Swift ≥ 3.0
let seconds = 4.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
// Put your code which should be executed with a delay here
}
Swift ≥ 5.5 in an async context:
func foo() async {
try await Task.sleep(nanoseconds: UInt64(seconds * Double(NSEC_PER_SEC)))
// Put your code which should be executed with a delay here
}
Swift < 3.0
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 4 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
// Put your code which should be executed with a delay here
}
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.
Comparison between different approaches in swift 3.0
1. Sleep
This method does not have a call back. Put codes directly after this line to be executed in 4 seconds. It will stop user from iterating with UI elements like the test button until the time is gone. Although the button is kind of frozen when sleep kicks in, other elements like activity indicator is still spinning without freezing. You cannot trigger this action again during the sleep.
sleep(4)
print("done")//Do stuff here
2. Dispatch, Perform and Timer
These three methods work similarly, they are all running on the background thread with call backs, just with different syntax and slightly different features.
Dispatch is commonly used to run something on the background thread. It has the callback as part of the function call
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
print("done")
})
Perform is actually a simplified timer. It sets up a timer with the delay, and then trigger the function by selector.
perform(#selector(callback), with: nil, afterDelay: 4.0)
func callback() {
print("done")
}}
And finally, timer also provides ability to repeat the callback, which is not useful in this case
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)
func callback() {
print("done")
}}
For all these three method, when you click on the button to trigger them, UI will not freeze and you are allowed to click on it again. If you click on the button again, another timer is set up and the callback will be triggered twice.
In conclusion
None of the four method works good enough just by themselves. sleep will disable user interaction, so the screen "freezes"(not actually) and results bad user experience. The other three methods will not freeze the screen, but you can trigger them multiple times, and most of the times, you want to wait until you get the call back before allowing user to make the call again.
So a better design will be using one of the three async methods with screen blocking. When user click on the button, cover the entire screen with some translucent view with a spinning activity indicator on top, telling user that the button click is being handled. Then remove the view and indicator in the call back function, telling user that the the action is properly handled, etc.
In Swift 4.2 and Xcode 10.1
You have 4 ways total to delay. Out of these option 1 is preferable to call or execute a function after some time. The sleep() is least case in use.
Option 1.
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.yourFuncHere()
}
//Your function here
func yourFuncHere() {
}
Option 2.
perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)
//Your function here
#objc func yourFuncHere2() {
print("this is...")
}
Option 3.
Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)
//Your function here
#objc func yourFuncHere3() {
}
Option 4.
sleep(5)
If you want to call a function after some time to execute something don't use sleep.
I agree with Palle that using dispatch_after is a good choice here. But you probably don't like the GCD calls as they are quite annoying to write. Instead you can add this handy helper:
public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: #escaping () -> Void) {
let dispatchTime = DispatchTime.now() + seconds
dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}
public enum DispatchLevel {
case main, userInteractive, userInitiated, utility, background
var dispatchQueue: DispatchQueue {
switch self {
case .main: return DispatchQueue.main
case .userInteractive: return DispatchQueue.global(qos: .userInteractive)
case .userInitiated: return DispatchQueue.global(qos: .userInitiated)
case .utility: return DispatchQueue.global(qos: .utility)
case .background: return DispatchQueue.global(qos: .background)
}
}
}
Now you simply delay your code on a background thread like this:
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Delaying code on the main thread is even simpler:
delay(bySeconds: 1.5) {
// delayed code, by default run in main thread
}
If you prefer a Framework that also has some more handy features then checkout HandySwift. You can add it to your project via SwiftPM then use it exactly like in the examples above:
import HandySwift
delay(by: .seconds(1.5)) {
// delayed code
}
You can also do this with Swift 3.
Perform the function after delay like so.
override func viewDidLoad() {
super.viewDidLoad()
self.perform(#selector(ClassName.performAction), with: nil, afterDelay: 2.0)
}
#objc func performAction() {
//This function will perform after 2 seconds
print("Delayed")
}
NSTimer
The answer by #nneonneo suggested using NSTimer but didn't show how to do it. This is the basic syntax:
let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)
Here is a very simple project to show how it might be used. When a button is pressed it starts a timer that will call a function after a delay of half a second.
import UIKit
class ViewController: UIViewController {
var timer = NSTimer()
let delay = 0.5
// start timer when button is tapped
#IBAction func startTimerButtonTapped(sender: UIButton) {
// cancel the timer in case the button is tapped multiple times
timer.invalidate()
// start the timer
timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)
}
// function to be called after the delay
func delayedAction() {
print("action has started")
}
}
Using dispatch_time (as in Palle's answer) is another valid option. However, it is hard to cancel. With NSTimer, to cancel a delayed event before it happens, all you need to do is call
timer.invalidate()
Using sleep is not recommended, especially on the main thread, since it stops all the work being done on the thread.
See here for my fuller answer.
Try the following implementation in Swift 3.0
func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
Usage
delayWithSeconds(1) {
//Do something
}
If you need to set a delay of less than a second, it is not necessary to set the .seconds parameter. I hope this is useful to someone.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
// your code hear
})
You can create extension to use delay function easily (Syntax: Swift 4.2+)
extension UIViewController {
func delay(_ delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
}
How to use in UIViewController
self.delay(0.1, closure: {
//execute code
})
I believe the simplest and latest way of doing a 4 seconds timer is:
Task {
// Do something
// Wait for 4 seconds
try await Task.sleep(nanoseconds: 4_000_000_000)
}
It uses Swift 5.5's new concurrency.
If your code is already running in a background thread, pause the thread using this method in Foundation: Thread.sleep(forTimeInterval:)
For example:
DispatchQueue.global(qos: .userInitiated).async {
// Code is running in a background thread already so it is safe to sleep
Thread.sleep(forTimeInterval: 4.0)
}
(See other answers for suggestions when your code is running on the main thread.)
DispatchQueue.global(qos: .background).async {
sleep(4)
print("Active after 4 sec, and doesn't block main")
DispatchQueue.main.async{
//do stuff in the main thread here
}
}
To create a simple time delay, you can import Darwin and then use sleep(seconds) to do the delay. That only takes whole seconds, though, so for more precise measurements you can import Darwin and use usleep(millionths of a second) for very precise measurement. To test this, I wrote:
import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")
Which prints, then waits for 1 sec and prints, then waits for 0.4 sec then prints. All worked as expected.
Using DispatchQueue's .asyncAfter method you can execute code after given time. So, e.g. execute ... on main thread after 1 second looks like this:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { ... }
Using my handy Delay wrapper struct you can execute it in more fancy way:
struct Delay {
#discardableResult
init(_ timeInterval: TimeInterval, queue: DispatchQueue = .main, executingBlock: #escaping () -> Void) {
queue.asyncAfter(deadline: .now() + timeInterval, execute: executingBlock)
}
}
Usage:
Delay(0.4) { ... }
Swift 5<
Using Task.sleep will not block any code other than the task at hand, and it's pretty straightforward.
//Delay task by 4 seconds:
Task {
try await Task.sleep(nanoseconds: 4000000000)
//Execute your code here
}
This is a simpler way of adding a delay that doesn't affect thread execution.
let continueTime: Date = Calendar.current.date(byAdding: .second, value: 30, to: Date())!
while (Date() < continueTime) {
//DO NOTHING
}
As an alternative solution to the previously proposed options, you can use a delay based on the DispatchGroup class, which is designed to synchronise the execution of several asynchronous tasks:
print("Start")
print(Date())
let delay = DispatchTimeInterval.seconds(3)
let group = DispatchGroup()
group.enter()
_ = group.wait(timeout: .now() + delay)
print("Finish")
print(Date())
Where the enter() method is used to explicitly indicate that the execution of the group code has begun and wait(timeout:) method to wait for group tasks to complete. Of course, in this example this will never happen, for this a timeout is specified, which is equal to the required delay.
It is convenient to use it as a ready-made helper:
public class DispatchWait {
private init () { }
public static func `for` (_ interval: DispatchTimeInterval) {
let group = DispatchGroup()
group.enter()
_ = group.wait(timeout: .now().advanced(by: interval))
}
}
An example of using the DispatchWait:
print("Start")
print(Date())
DispatchWait.for(.seconds(3))
print("Finish")
print(Date())
Unfortunately, I cannot say what is the accuracy of this delay, and what is the probability that the wait(timeout:) method will allow further execution of the program much later than the specified delay.
Also, this solution allows you to delay the code in the current queue, without having to execute it in a separate closure.
this is the simplest
delay(0.3, closure: {
// put her any code you want to fire it with delay
button.removeFromSuperview()
})
Related
I have a function that runs for a long time, and I'd like to give it a cap of 10 seconds if possible. I'd also like to implement a Cancel button for the user to press as well.
I tried simply checking the status of a var abort while it calculates, but it just continues working. I'm not very skilled with Grand Central Dispatch and timers and don't know where to go from here.
...
var abort = false
#objc func abortCalculations() {
abort = true
}
func calculate() {
abort = false
let _ = Timer(timeInterval: 10, target: self, selector: #selector(abortCalculations), userInfo: nil, repeats: false)
dispatchGroup.enter()
DispatchQueue.global(qos: .userInteractive).async {
while !abort {
x += 1 // for example, just something that repeats
// when this actually finishes, it leaves the dispatch group
}
}
}
...
I'd like the function calculate() to finish when the timer stops it (or when a user presses a button, in the future). Instead, it continues to run and the function abortCalculations() is never called.
Instead of using a Timer, you can use a similar DispatchQueue to set your abort flag.
var abortFlagItem = DispatchWorkItem(block: {
self.abort = true
})
DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: abortFlagItem)
At any point if you want to cancel, you can call abortFlagItem.cancel()
How to call function on time? Also How to Call only once time in swift 3?
i need to call one function one time only, so how can possible to call function only one time like after 3 seconds?
You can do something like,
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
yourFunction() // call your functin here
}
You can also use DispatchQueue.global().asyncAfter if you don't want to perform your task on main thread!
And refer this post to manage it one time only!
private let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print( "Do This Once!" )
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
yourFunction() // call your functin here
}
}
It will execute your method only once and delay your method for 3 seconds.
For start timer:
var timerUpdateArray:Timer!
func callTimer(){
self.timerUpdateArray = Timer.scheduledTimer(timeInterval: 1,
target: self, selector: #selector(yourFunc),
userInfo: nil, repeats: true)
}
For stop timer:
self.timerUpdateArray.invalidate()
I have a UICOllectionView that contains several UIIMages (No use of storyboards). When the user taps an Image a view is placed inside of the specific UIImage which contains an information for the user. I want that this view deletes it self automatically after 2 seconds. Can someone please give me an example how to achieve that. I didn't find any solution. I know that I can dismiss a view very easy by saying View.removeFromSuperView(). Can someone please make a short example PLEASE
Thanks
You can execute a block of code after 2 seconds using Grand Central Dispatch
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// code to remove your view
}
Try this :
let viewss = UIView()
In ViewDidLoad
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
self.viewss.isHidden = true
}
You can create a performAfter function which uses Swift 3 GCD asyncAfter function internally like
func performAfter(delay: TimeInterval, completion: #escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
completion()
}
}
and use
performAfter(delay: 2) {
print("task to be done")
}
I have a UIControl that calls a function after 0.5 seconds depending on how many times the user presses it.
(Eg 1 press calls f1(), 2 presses calls f2(), 3 presses calls f3())
So basically I need to set a timer when a user presses the Control. If the Control is not pressed for 0.5 seconds then create a dialog. I have tried using a DispatchQueue, but when it gets to the point of making the dialog, it takes several seconds. I think it is because it is being called concurrently instead of on the main thread (apologies if poor terminology).
self.operationQueue.cancelAllOperations() //To cancel previous queues
self.mainAsyncQueue = DispatchQueue(label: "bubblePressed" + String(describing: DispatchTime.now()), qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent)
let time = DispatchTime.now()
self.currentTime = time
self.mainAsyncQueue!.asyncAfter(deadline: time + 0.5){
guard self.currentTime == time else {
return
}
let tempOperation = BlockOperation(block:{
self.displayDialog()
})
self.operationQueue.addOperation(tempOperation)
}
operationQueue and mainAsycQueue are defined in viewDidLoad as
self.currentTime = DispatchTime.now()
self.operationQueue = OperationQueue()
How can I call my function displayDialog() in the main thread so that it loads faster?
Based on the question title, answer is:
let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
//update UI here
self.displayDialog()
}
or
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.displayDialog()
}
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
Use below func, it executes the func in the main thread and no other action will perform during this execution.
DispatchQueue.main.async {
self.displayDialog()
}
In earlier versions of Swift, one could create a delay with the following code:
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 4 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
//put your code which should be executed with a delay here
}
But now, in Swift 3, Xcode automatically changes 6 different things but then the following error appears: "Cannot convert DispatchTime.now to expected value dispatch_time_t aka UInt64."
How can one create a delay before running a sequence of code 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.
I like one-line notation for GCD, it's more elegant:
DispatchQueue.main.asyncAfter(deadline: .now() + 42.0) {
// do stuff 42 seconds later
}
Also, in iOS 10 we have new Timer methods, e.g. block initializer:
(so delayed action may be canceled)
let timer = Timer.scheduledTimer(withTimeInterval: 42.0, repeats: false) { (timer) in
// do stuff 42 seconds later
}
Btw, keep in mind: by default, timer is added to the default run loop mode. It means timer may be frozen when the user is interacting with the UI of your app (for example, when scrolling a UIScrollView)
You can solve this issue by adding the timer to the specific run loop mode:
RunLoop.current.add(timer, forMode: .common)
At this blog post you can find more details.
Try the following function implemented in Swift 3.0 and above
func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
Usage
delayWithSeconds(1) {
//Do something
}
Try the below code for delay
//MARK: First Way
func delayForWork() {
delay(3.0) {
print("delay for 3.0 second")
}
}
delayForWork()
// MARK: Second Way
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// your code here delayed by 0.5 seconds
}
One way is to use DispatchQueue.main.asyncAfter as a lot of people have answered.
Another way is to use perform(_:with:afterDelay:). More details here
perform(#selector(delayedFunc), with: nil, afterDelay: 3)
#IBAction func delayedFunc() {
// implement code
}
Most common things to use are asyncAfter() and Timer. But if blocking thread is OK, then there is an option:
sleep(3) // in seconds
usleep // in 1/million of second
For asynchronous programming (Swift 5.5) pausing in func looks like this:
func someAsyncFunc() async {
await Task.sleep(2_000_000_000) // Two seconds
// Code to be executed with a delay here
}
//Runs function after x seconds
public static func runThisAfterDelay(seconds: Double, after: #escaping () -> Void) {
runThisAfterDelay(seconds: seconds, queue: DispatchQueue.main, after: after)
}
public static func runThisAfterDelay(seconds: Double, queue: DispatchQueue, after: #escaping () -> Void) {
let time = DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
queue.asyncAfter(deadline: time, execute: after)
}
//Use:-
runThisAfterDelay(seconds: x){
//write your code here
}