Voglio mettere in pausa la mia app a un certo punto. In altre parole, voglio che la mia app esegua il codice, ma poi a un certo punto, metti in pausa per 4 secondi, quindi proseguo con il resto del codice. Come posso fare questo?
Sto usando Swift.
Voglio mettere in pausa la mia app a un certo punto. In altre parole, voglio che la mia app esegua il codice, ma poi a un certo punto, metti in pausa per 4 secondi, quindi proseguo con il resto del codice. Come posso fare questo?
Sto usando Swift.
Risposte:
Invece di uno sleep, che bloccherà il tuo programma se chiamato dal thread dell'interfaccia utente, considera l'utilizzo NSTimer
o un timer di invio.
Ma, se hai davvero bisogno di un ritardo nel thread corrente:
do {
sleep(4)
}
Questo utilizza la sleep
funzione di UNIX.
L'uso di un dispatch_after
blocco è nella maggior parte dei casi meglio dell'uso sleep(time)
poiché il thread su cui viene eseguita la sospensione viene bloccato dall'altro lavoro. quando si utilizza dispatch_after
il thread su cui si lavora non viene bloccato, quindi può fare altri lavori nel frattempo.
Se stai lavorando sul thread principale della tua applicazione, l'utilizzo sleep(time)
è negativo per l'esperienza utente della tua app poiché l'interfaccia utente non risponde in quel periodo.
Invia dopo pianifica l'esecuzione di un blocco di codice invece di bloccare il thread:
let seconds = 4.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
// Put your code which should be executed with a delay here
}
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
}
.now() + .seconds(4)
restituisce un errore:expression type is ambiguous without more context
.now() + .seconds(5)
è semplicemente.now() + 5
Confronto tra diversi approcci in swift 3.0
1. Dormire
Questo metodo non ha una richiamata. Inserisci i codici direttamente dopo questa riga per eseguirli in 4 secondi. Impedirà all'utente di scorrere gli elementi dell'interfaccia utente come il pulsante di test fino a quando non è trascorso il tempo. Sebbene il pulsante sia un po 'congelato quando entra in funzione il sonno, altri elementi come l'indicatore di attività continuano a girare senza congelamento. Non è possibile attivare nuovamente questa azione durante il sonno.
sleep(4)
print("done")//Do stuff here
2. Spedizione, esecuzione e timer
Questi tre metodi funzionano in modo simile, sono tutti in esecuzione sul thread in background con callback, solo con sintassi diversa e caratteristiche leggermente diverse.
La spedizione è comunemente usata 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")
})
Perform è in realtà un timer semplificato. Imposta un timer con il ritardo, quindi attiva la funzione tramite selettore.
perform(#selector(callback), with: nil, afterDelay: 4.0)
func callback() {
print("done")
}}
E infine, il timer offre anche la possibilità di ripetere la richiamata, il che non è utile in questo caso
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)
func callback() {
print("done")
}}
Per tutti e tre questi metodi, quando si fa clic sul pulsante per attivarli, l'interfaccia utente non si blocca e si può fare nuovamente clic su di esso. Se si fa di nuovo clic sul pulsante, viene impostato un altro timer e la richiamata verrà attivata due volte.
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 si traduce in un'esperienza utente negativa. Gli altri tre metodi non bloccano lo schermo, ma è possibile attivarli più volte e, la maggior parte delle volte, si desidera attendere fino a quando non si riceve nuovamente la chiamata prima di consentire all'utente di effettuare nuovamente la chiamata.
Quindi un design migliore utilizzerà uno dei tre metodi asincroni con 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 alto, che indica all'utente che il clic del pulsante viene gestito. Quindi rimuovere la vista e l'indicatore nella funzione di richiamata, dicendo all'utente che l'azione è gestita correttamente, ecc.
@objc
davanti alla funzione di richiamata per l' perform(...)
opzione. In questo modo:@objc func callback() {
Sono d'accordo con Palle che l'utilizzo dispatch_after
è una buona scelta qui. Ma probabilmente non ti piacciono le chiamate GCD in quanto sono abbastanza fastidiose da scrivere . Invece puoi aggiungere questo utile aiuto :
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 semplicemente ritardi 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 ha anche alcune funzionalità più utili, dai un'occhiata a HandySwift . Puoi aggiungerlo al tuo progetto tramite Cartagine o Accio, quindi usarlo esattamente come negli esempi sopra:
import HandySwift
delay(by: .seconds(1.5)) {
// delayed code
}
DispatchTime
non era disponibile in Swift 2. Era let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
prima.
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")
}
In Swift 4.2 e Xcode 10.1
Hai 4 modi per ritardare. Di queste opzioni 1 è preferibile chiamare o eseguire una funzione dopo qualche tempo. Il sleep () è il caso minimo in uso.
Opzione 1.
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.yourFuncHere()
}
//Your function here
func yourFuncHere() {
}
Opzione 2.
perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)
//Your function here
@objc func yourFuncHere2() {
print("this is...")
}
Opzione 3
Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)
//Your function here
@objc func yourFuncHere3() {
}
Opzione 4
sleep(5)
Se si desidera chiamare una funzione dopo qualche tempo per eseguire qualcosa, non utilizzare la modalità sospensione .
La risposta di @nneonneo ha suggerito di usare NSTimer
ma non ha mostrato come farlo. Questa è la sintassi 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 usato. 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 dispatch_time
(come nella risposta di Palle ) è un'altra opzione valida. Tuttavia, è difficile da annullare . Con NSTimer
, per annullare un evento ritardato prima che accada, tutto ciò che devi fare è chiamare
timer.invalidate()
L'uso sleep
non è raccomandato, specialmente sul thread principale, poiché interrompe tutto il lavoro svolto sul thread.
Vedi qui per la mia risposta più completa.
È possibile creare l'estensione per utilizzare facilmente la funzione di ritardo (Sintassi: 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)
}
}
Come utilizzare in UIViewController
self.delay(0.1, closure: {
//execute code
})
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
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}
è probabilmente più corretto ✌️
Se il codice è già in esecuzione in un thread in background, sospendi il thread utilizzando questo metodo in Foundation :Thread.sleep(forTimeInterval:)
Per esempio:
DispatchQueue.global(qos: .userInitiated).async {
// Code is running in a background thread already so it is safe to sleep
Thread.sleep(forTimeInterval: 4.0)
}
(Vedi altre risposte per suggerimenti quando il tuo codice è in esecuzione sul thread principale.)
Per creare un semplice ritardo, è possibile importare Darwin e quindi utilizzare la sospensione (secondi) per eseguire il ritardo. Ciò richiede solo interi secondi, quindi per misure più precise puoi importare Darwin e usare usleep (milionesimi di secondo) per misure molto precise. Per provare questo, ho scritto:
import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")
Quale stampa, quindi attende 1 secondo e stampa, quindi attende 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()
})