如何在Swift中创建延迟?

Sch*_*999 230 xcode delay swift

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

我正在使用Swift.

Pal*_*lle 313

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

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

斯威夫特≥3.0

let seconds = 4.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
    // Put your code which should be executed with a delay here
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特<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
}
Run Code Online (Sandbox Code Playgroud)

  • 你不再需要添加`.now()+ .seconds(5)`它只是`.now()+ 5` (13认同)
  • 有可能使用 gcd 来执行周期性任务,这些任务在[苹果开发人员文档中的这篇文章](https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html #//apple_ref/doc/uid/TP40008091-CH103-SW2)但我建议使用 NSTimer。[这个答案](http://stackoverflow.com/a/24007862/2557145)展示了如何在swift中准确地做到这一点。 (2认同)
  • 代码已更新为Xcode 8.2.1.以前`.now()+ .seconds(4)`给出错误:`表达式类型是不明确的,没有更多的上下文 (2认同)

nne*_*neo 240

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

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

do {
    sleep(4)
}
Run Code Online (Sandbox Code Playgroud)

这使用了sleepUNIX中的函数.

  • 感谢您保持"不要这样做"的序言简短. (26认同)
  • usleep()也适用,如果你需要更精确的1秒分辨率. (14认同)
  • usleep()需要百万分之一秒,所以睡眠(1000000)将睡眠1秒 (12认同)
  • 睡眠工作没有导入达尔文,不知道这是否是一个变化 (4认同)
  • 我使用sleep()来模拟长时间运行的后台进程. (2认同)
  • 我更喜欢 Thread.sleep(forTimeInterval: 4.0) ,它允许您使用常规 TimeInterval 指定持续时间。 (2认同)

Fan*_*ing 40

swift 3.0中不同方法的比较

1.睡觉

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

sleep(4)
print("done")//Do stuff here
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

2.派遣,执行和计时器

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

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

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
    print("done")
})
Run Code Online (Sandbox Code Playgroud)

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

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

func callback() {
    print("done")
}}
Run Code Online (Sandbox Code Playgroud)

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

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


func callback() {
    print("done")
}}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

结论

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

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

  • 请注意,在 Swift 4 中,关闭 objc 推理,您需要在回调函数前添加 `@objc` 以获取 `perform(...)` 选项。像这样:`@objc func callback() {` (2认同)

Jee*_*hut 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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您只需将代码延迟到这样的后台线程:

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

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

delay(bySeconds: 1.5) { 
    // delayed code, by default run in main thread
}
Run Code Online (Sandbox Code Playgroud)

如果你更喜欢一个也有一些更方便功能的框架,那么请查看HandySwift.您可以通过Carthage将其添加到项目中,然后使用它与上面的示例完全相同:

import HandySwift    

delay(by: .seconds(1.5)) { 
    // delayed code
}
Run Code Online (Sandbox Code Playgroud)


Cyr*_*cia 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")
        }
Run Code Online (Sandbox Code Playgroud)


Sur*_*gch 19

的NSTimer

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

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

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

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

使用dispatch_time(如Palle的答案)是另一个有效选项.但是,很难取消.有了NSTimer,要在事件发生之前取消延迟事件,您需要做的就是打电话

timer.invalidate()
Run Code Online (Sandbox Code Playgroud)

使用sleep不推荐,尤其是在主线程,因为它停止的线程上正在做的所有工作.

请看这里我更全面的答案.


Leg*_*ang 16

我相信做 4 秒计时器的最简单和最新的方法是:

Task { 
    // Do something

    // Wait for 4 seconds
    try await Task.sleep(nanoseconds: 4_000_000_000) 

}
Run Code Online (Sandbox Code Playgroud)

它使用Swift 5.5 的新并发性。

苹果的同意


iOS*_*iOS 12

在Swift 4.2和Xcode 10.1中

您总共有4种方法可以延迟。在这些选项1中,最好在一段时间后调用或执行一个函数。的睡眠()是在使用至少情况。

选项1。

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

}
Run Code Online (Sandbox Code Playgroud)

选项2。

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

//Your function here  
@objc func yourFuncHere2() {
    print("this is...")
}
Run Code Online (Sandbox Code Playgroud)

选项3。

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

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

}
Run Code Online (Sandbox Code Playgroud)

选项4。

sleep(5)
Run Code Online (Sandbox Code Playgroud)

如果您想在一段时间后调用某个函数来执行某些操作,请不要使用sleep


Rob*_*ler 8

编辑:

我强烈建议开始使用Swift Concurrency。借助 Swift Concurrency,您可以使用 轻松暂停当前任务Task.sleep


使用DispatchQueue.asyncAfter方法,您可以在给定时间后执行代码。因此,例如...1 秒后在主线程上执行如下所示:

DispatchQueue.main.asyncAfter(deadline: .now() + 1) { ... }
Run Code Online (Sandbox Code Playgroud)

使用我方便的Delay包装结构,您可以以更奇特的方式执行它:

struct Delay {

    @discardableResult
    init(_ timeInterval: TimeInterval, queue: DispatchQueue = .main, executingBlock: @escaping () -> Void) {
        queue.asyncAfter(deadline: .now() + timeInterval, execute: executingBlock)
    }

}
Run Code Online (Sandbox Code Playgroud)

用法:

Delay(0.4) { ... }
Run Code Online (Sandbox Code Playgroud)


Vak*_*kas 6

在Swift 3.0中尝试以下实现

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

用法

delayWithSeconds(1) {
   //Do something
}
Run Code Online (Sandbox Code Playgroud)


Eth*_*son 6

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

import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")
Run Code Online (Sandbox Code Playgroud)

打印,然后等待 1 秒并打印,然后等待 0.4 秒然后打印。一切都按预期进行。


Boo*_*rin 6

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

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
        // your code hear
})
Run Code Online (Sandbox Code Playgroud)


eon*_*ist 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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 做`DispatchQueue.main.asyncAfter(截止日期:.now()+ 4){/*做东西*/}`可能更正确✌️ (3认同)

Rob*_*art 5

如果您的代码已经在后台线程中运行,请在 Foundation 中使用此方法暂停线程:Thread.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)
}
Run Code Online (Sandbox Code Playgroud)

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


Har*_*kar 5

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

如何在UIViewController中使用

self.delay(0.1, closure: {
   //execute code
})
Run Code Online (Sandbox Code Playgroud)


小智 5

斯威夫特 5<

使用Task.sleep不会阻止除手头任务之外的任何代码,而且非常简单。

//Delay task by 4 seconds:

Task {
    try await Task.sleep(nanoseconds: 4000000000)
    //Execute your code here
}
Run Code Online (Sandbox Code Playgroud)