Come creare un ritardo in Swift?

Voglio mettere in pausa la mia app ad un certo punto. In altre parole, voglio che la mia app esegua il codice, ma a un certo punto, sospendi per 4 secondi e poi continua con il resto del codice. Come posso fare questo?

Sto usando Swift.

Invece di una sospensione, che bloccherà il tuo programma se chiamato dal thread dell’interfaccia utente, considera l’utilizzo di NSTimer o di un timer di spedizione.

Ma, se hai davvero bisogno di un ritardo nel thread corrente:

 ... { sleep(4) } 

Questo utilizza la funzione sleep di UNIX.

Nella maggior parte dei casi l’utilizzo di un blocco dispatch_after è migliore rispetto all’utilizzo di sleep(time) poiché il thread su cui viene eseguito lo sleep è bloccato da altri lavori. quando si usa dispatch_after il thread su cui si lavora non viene bloccato in modo che possa svolgere altri lavori nel frattempo.
Se stai lavorando sul thread principale della tua applicazione, l’uso di sleep(time) è positivo per l’esperienza utente della tua app in quanto l’interfaccia utente non risponde durante quel periodo.

Invio dopo pianifica l’esecuzione di un blocco di codice anziché congelare il thread:

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 } 

Sono d’accordo con Palle che usare dispatch_after è una buona scelta qui. Ma probabilmente non ti piacciono le chiamate GCD perché sono abbastanza fastidiose da scrivere . Invece puoi aggiungere questo pratico aiutante :

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

Ora si ritarda semplicemente il codice su un thread in background come questo:

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

Ritardare il codice sul thread principale è ancora più semplice:

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

Se preferisci un Framework che abbia anche alcune funzionalità a portata di mano, controlla HandySwift . Puoi aggiungerlo al tuo progetto tramite Carthage, quindi utilizzarlo esattamente come negli esempi sopra:

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

Confronto tra diversi approcci in Swift 3.0

1. Dormire

Questo metodo non ha una richiamata. Inserisci i codici direttamente dopo che questa riga deve essere eseguita in 4 secondi. Si fermerà l’utente dal iterare con elementi dell’interfaccia utente come il pulsante di test fino a quando il tempo è passato. Sebbene il pulsante sia un po ‘congelato quando il sonno entra in funzione, altri elementi come l’indicatore di attività continuano a ruotare senza congelarsi. Non è ansible triggersre nuovamente questa azione durante il sonno.

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

inserisci la descrizione dell'immagine qui

2. Spedizione, esecuzione e timer

Questi tre metodi funzionano in modo simile, sono tutti in esecuzione sul thread in background con richiamate, solo con syntax diverse e caratteristiche leggermente diverse.

Dispatch è comunemente usato per eseguire qualcosa sul thread in background. Ha il callback come parte della chiamata di funzione

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

Eseguire è in realtà un timer semplificato. Imposta un timer con il ritardo, quindi triggers la funzione tramite selettore.

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

Infine, il timer offre anche la possibilità di ripetere la richiamata, che in questo caso non è utile

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

Per tutti questi tre metodi, quando fai clic sul pulsante per triggersrli, l’interfaccia utente non si blocca e puoi fare nuovamente clic su di essa. Se fai nuovamente clic sul pulsante, viene impostato un altro timer e la richiamata verrà triggersta due volte.

inserisci la descrizione dell'immagine qui

In conclusione

Nessuno dei quattro metodi funziona abbastanza bene da solo. sleep disabiliterà l’interazione dell’utente, quindi lo schermo si ” blocca ” (non effettivamente) e risulta un’esperienza utente negativa. Gli altri tre metodi non congelano lo schermo, ma è ansible triggersrli più volte, e la maggior parte delle volte, si desidera attendere fino al richiamo della chiamata prima di consentire all’utente di effettuare nuovamente la chiamata.

Quindi un design migliore userà uno dei tre metodi asincroni con il blocco dello schermo. Quando l’utente fa clic sul pulsante, copre l’intero schermo con una vista traslucida con un indicatore di attività rotante in cima, dicendo all’utente che il clic del pulsante viene gestito. Quindi rimuovere la vista e l’indicatore nella funzione di richiamata, comunicando all’utente che l’azione è gestita correttamente, ecc.

NSTimer

La risposta di @nneonneo suggeriva di usare NSTimer ma non mostrava come farlo. Questa è la syntax di base:

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

Ecco un progetto molto semplice per mostrare come potrebbe essere utilizzato. Quando viene premuto un pulsante, avvia un timer che chiamerà una funzione dopo un ritardo di mezzo secondo.

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

L’uso di dispatch_time (come nella risposta di Palle ) è un’altra opzione valida. Tuttavia, è difficile da cancellare . Con NSTimer , per annullare un evento in ritardo prima che accada, tutto ciò che devi fare è chiamare

 timer.invalidate() 

L’uso di sleep non è raccomandato, specialmente sul thread principale, poiché interrompe tutto il lavoro sul thread.

Vedi qui per la mia risposta più completa.

Puoi farlo anche con Swift 3.

Eseguire la funzione dopo il ritardo in questo modo.

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

Prova la seguente implementazione in 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 } } 

Per creare un intervallo di tempo semplice, è ansible importare Darwin e quindi utilizzare sleep (secondi) per eseguire il ritardo. Ciò richiede solo interi secondi, tuttavia, così per misurazioni più precise è ansible importare Darwin e usare usleep (milionesimi di secondo) per una misurazione molto precisa. Per testare questo, ho scritto:

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

Che stampa, quindi attende 1 secondo e stampa, quindi attende per 0,4 secondi quindi stampa. Tutto ha funzionato come previsto.

questo è il più semplice

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