如何在Swift中创建延迟?

时间:2014-12-17 02:58:30

标签: xcode swift delay

我想暂时停止我的应用。换句话说,我希望我的应用程序执行代码,但在某个时刻,暂停4秒,然后继续执行其余代码。我怎么能这样做?

我正在使用Swift。

14 个答案:

答案 0 :(得分:292)

在大多数情况下,使用dispatch_after块比使用sleep(time)更好,因为执行睡眠的线程无法执行其他工作。当使用dispatch_after时,处理的线程不会被阻止,因此它可以在此期间执行其他工作。
如果您正在处理应用程序的主线程,使用sleep(time)会对应用程序的用户体验造成不利影响,因为在此期间UI无响应。

在调度代码块的执行而不是冻结线程之后调度:

Swift≥3.0

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
    // Put your code which should be executed with a delay here
})

斯威夫特< 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
}

答案 1 :(得分:219)

如果从UI线程调用会锁定程序,而不是睡眠,请考虑使用NSTimer或调度计时器。

但是,如果你真的需要在当前线程中延迟:

... {
    sleep(4)
}

这使用UNIX中的sleep函数。

答案 2 :(得分:38)

swift 3.0中不同方法之间的比较

<强> 1。睡眠

此方法没有回叫。在此行之后直接放置代码将在4秒内执行。它将阻止用户使用测试按钮之类的UI元素进行迭代,直到时间消失。虽然按钮在睡眠开始时有点冻结,但活动指示器等其他元素仍在旋转而不会冻结。在睡眠期间,您无法再次触发此操作。

sleep(4)
print("done")//Do stuff here

enter image description here

<强> 2。派遣,执行和计时器

这三种方法的工作方式类似,它们都在带有回调的后台线程上运行,只是语法不同,功能略有不同。

Dispatch通常用于在后台线程上运行某些东西。它将回调作为函数调用的一部分

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
    print("done")
})

Perform实际上是一个简化的计时器。它设置一个带延迟的定时器,然后通过选择器触发该功能。

perform(#selector(callback), with: nil, afterDelay: 4.0)

func callback() {
    print("done")
}}

最后,计时器还提供了重复回调的功能,这在这种情况下无用

Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)


func callback() {
    print("done")
}}

对于所有这三种方法,当您单击按钮触发它们时,UI不会冻结,您可以再次单击它。如果再次单击该按钮,则会设置另一个计时器,并且将触发两次回调。

enter image description here

总结

四种方法中没有一种能够单独使用。 sleep将禁用用户互动,因此屏幕&#34; 冻结&#34;(实际上并非如此)并导致糟糕的用户体验。其他三种方法不会冻结屏幕,但您可以多次触发它们,而且大多数情况下,您希望等到收到回叫之后才允许用户再次拨打电话。

因此,更好的设计将使用屏幕阻止的三种异步方法之一。当用户点击按钮时,在整个屏幕上覆盖一些半透明视图,顶部有旋转活动指示器,告诉用户正在处理按钮点击。然后删除回调函数中的视图和指示器,告诉用户该操作已正确处理等等。

答案 3 :(得分:30)

我同意Palle的观点,在这里使用dispatch_after 是一个不错的选择。但是你可能不喜欢GCD调用,因为它们很难写。相反,您可以添加方便助手

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)
        }
    }
}

现在,您只需在后台线程上延迟代码,就像这样:

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

延迟主线程上的代码甚至更简单:

delay(bySeconds: 1.5) { 
    // delayed code, by default run in main thread
}

如果您更喜欢具有更强大功能的框架,请结帐 HandySwift 。您可以将其添加到项目通过CarthageAccio ,然后使用它,就像上面的示例中一样:

import HandySwift    

delay(by: .seconds(1.5)) { 
    // delayed code
}

答案 4 :(得分:24)

您也可以使用Swift 3执行此操作。

延迟后执行此功能。

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")
        }

答案 5 :(得分:18)

NSTimer

@nneonneo的回答建议使用NSTimer,但没有说明如何做到这一点。这是基本语法:

let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)

这是一个非常简单的项目,展示如何使用它。当按下一个按钮时,它会启动一个定时器,在延迟半秒后调用一个函数。

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")
    }
}

使用dispatch_time(如在Palle's answer中)是另一个有效选项。但是,它是hard to cancel。使用NSTimer,要在事件发生之前取消延迟事件,您只需要调用

timer.invalidate()

建议不要使用sleep,尤其是在主线程上,因为它会停止在线程上完成的所有工作。

请参阅here以获得更全面的答案。

答案 6 :(得分:6)

在Swift 3.0中尝试以下实现

func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { 
        completion()
    }
}

用法

delayWithSeconds(1) {
   //Do something
}

答案 7 :(得分:5)

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
    }
}

答案 8 :(得分:5)

在Swift 4.2和Xcode 10.1中

您总共有4种延迟方式。在所有选项1 中,最好在一段时间后调用或执行一个函数。在所有 sleep()中,使用的情况最少。

选项1。

DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    self.yourFuncHere()
}
//Your function here    
func yourFuncHere() {

}

选项2。

perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)

//Your function here  
@objc func yourFuncHere2() {
    print("this is...")
}

选项3。

Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)

//Your function here  
@objc func yourFuncHere3() {

}

选项4。

sleep(5)

答案 9 :(得分:4)

如果需要设置小于一秒的延迟,则无需设置.seconds参数。我希望这对某人有用。

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
        // your code hear
})

答案 10 :(得分:2)

要创建简单的时间延迟,您可以导入Darwin,然后使用sleep(秒)来执行延迟。但是,这只需要整整一秒钟,因此对于更精确的测量,您可以导入Darwin并使用usleep(百万分之一秒)进行非常精确的测量。为了测试这个,我写道:

import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")

打印哪个,然后等待1秒并打印,然后等待0.4秒然后打印。一切都按预期工作。

答案 11 :(得分:0)

如果您的代码已经在后台线程中运行,请使用this method in FoundationThread.sleep(forTimeInterval:)

暂停线程

例如:

DispatchQueue.global(qos: .userInitiated).async {

    // Code is running in a background thread already so it is safe to sleep
    Thread.sleep(forTimeInterval: 4.0)
}

(当代码在主线程上运行时,请参阅其他答案以获取建议。)

答案 12 :(得分:0)

您可以创建扩展程序以轻松使用延迟功能(语法: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)
    }
}

如何在UIViewController中使用

self.delay(0.1, closure: {
   //execute code
})

答案 13 :(得分:-3)

这是最简单的

    delay(0.3, closure: {
        // put her any code you want to fire it with delay
        button.removeFromSuperview()   
    })