¿Cómo crear un retraso en Swift?

Quiero detener mi aplicación en cierto punto. En otras palabras, quiero que mi aplicación ejecute el código, pero luego, en cierto punto, pausa durante 4 segundos y luego continúa con el rest del código. ¿Cómo puedo hacer esto?

Estoy usando Swift.

En lugar de un reposo, que bloqueará su progtwig si se llama desde el subproceso de interfaz de usuario, considere usar NSTimer o un temporizador de despacho.

Pero, si realmente necesita un retraso en el hilo actual:

 ... { sleep(4) } 

Esto usa la función sleep de UNIX.

Usar un bloque dispatch_after es en la mayoría de los casos mejor que usar sleep(time) ya que el hilo en el que se realiza el sueño está bloqueado para realizar otro trabajo. cuando se utiliza dispatch_after el hilo en el que se trabaja no se bloquea, por lo que puede hacer otros trabajos mientras tanto.
Si está trabajando en el hilo principal de su aplicación, el uso de sleep(time) es malo para la experiencia del usuario de su aplicación, ya que la interfaz de usuario no responde durante ese tiempo.

Despachar después de los horarios la ejecución de un bloque de código en lugar de congelar el hilo:

Swift ≥ 3.0

 DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: { // 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 } 

Estoy de acuerdo con Palle en que usar dispatch_after es una buena opción aquí. Pero probablemente no te gusten las llamadas GCD ya que son bastante molestas de escribir . En su lugar, puede agregar este práctico ayudante :

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

Ahora simplemente demoras tu código en un hilo de fondo como este:

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

Retraso de código en el hilo principal es aún más simple:

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

Si prefiere un Framework que también tenga algunas características más útiles, realice la compra de HandySwift . Puede agregarlo a su proyecto a través de Cartago y luego usarlo exactamente como en los ejemplos anteriores:

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

Comparación entre diferentes enfoques en swift 3.0

1. Dormir

Este método no tiene una callback. Coloque los códigos directamente después de esta línea para que se ejecuten en 4 segundos. Se detendrá al usuario de iterar con elementos de la interfaz de usuario como el botón de prueba hasta que se acabe el tiempo. Aunque el botón se congela cuando suena el sueño, otros elementos, como el indicador de actividad, siguen girando sin congelarse. No puede disparar esta acción nuevamente durante el descanso.

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

enter image description here

2. Despacho, ejecución y temporizador

Estos tres métodos funcionan de manera similar, todos se ejecutan en el hilo de fondo con callbacks, solo con una syntax diferente y características ligeramente diferentes.

El envío se usa comúnmente para ejecutar algo en el hilo de fondo. Tiene la callback como parte de la llamada de función

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

Realizar es en realidad un temporizador simplificado. Configura un temporizador con la demora y luego activa la función por selector.

 perform(#selector(callback), with: nil, afterDelay: 4.0) func callback() { print("done") }} 

Y finalmente, el temporizador también proporciona la capacidad de repetir la callback, lo que no es útil en este caso

 Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false) func callback() { print("done") }} 

Para todos estos tres métodos, al hacer clic en el botón para activarlos, la IU no se congelará y podrá hacer clic de nuevo. Si vuelve a hacer clic en el botón, se configura otro temporizador y la callback se activará dos veces.

enter image description here

En conclusión

Ninguno de los cuatro métodos funciona lo suficientemente bien solo. sleep inhabilitará la interacción del usuario, por lo que la pantalla se ” congela ” (no en realidad) y los resultados son una mala experiencia para el usuario. Los otros tres métodos no congelarán la pantalla, pero puede activarlos varias veces, y la mayoría de las veces, desea esperar hasta que reciba la callback antes de permitir que el usuario realice la llamada nuevamente.

Entonces, un diseño mejor utilizará uno de los tres métodos asíncronos con locking de pantalla. Cuando el usuario hace clic en el botón, cubre toda la pantalla con una vista translúcida con un indicador de actividad giratoria en la parte superior, indicándole al usuario que se está manejando el clic del botón. Luego, elimine la vista y el indicador en la función de callback, indicándole al usuario que la acción se manejó adecuadamente, etc.

NSTimer

La respuesta de @nneonneo sugirió usar NSTimer pero no mostró cómo hacerlo. Esta es la syntax básica:

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

Aquí hay un proyecto muy simple para mostrar cómo se podría usar. Cuando se presiona un botón, se inicia un temporizador que llamará a una función después de un retraso de medio segundo.

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

Usar dispatch_time (como en la respuesta de Palle ) es otra opción válida. Sin embargo, es difícil de cancelar . Con NSTimer , para cancelar un evento retrasado antes de que suceda, todo lo que necesita hacer es llamar

 timer.invalidate() 

No se recomienda utilizar sleep , especialmente en el hilo principal, ya que detiene todo el trabajo realizado en el hilo.

Vea aquí mi respuesta más completa.

También puedes hacer esto con Swift 3.

Realice la función después de la demora como ese.

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

Pruebe la siguiente implementación en Swift 3.0

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

Uso

 delayWithSeconds(1) { //Do something } 
 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 } } 

Para crear un retraso de tiempo simple, puede importar Darwin y luego usar sleep (segundos) para realizar el retraso. Sin embargo, eso solo requiere segundos enteros, por lo que para mediciones más precisas puede importar Darwin y usar el modo sleep (millonésimas de segundo) para realizar mediciones muy precisas. Para probar esto, escribí:

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

Que imprime, luego espera 1 segundo e imprime, luego espera 0.4 segundos y luego imprime. Todo funcionó como se esperaba.

este es el más simple

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