Je veux mettre mon application en pause à un moment donné. En d'autres termes, je veux que mon application exécute le code, mais à un moment donné, marquez une pause de 4 secondes, puis continuez avec le reste du code. Comment puis-je faire ceci?
J'utilise Swift.
Au lieu d'une veille, qui bloquera votre programme s'il est appelé à partir du thread d'interface utilisateur, envisagez d'utiliser NSTimer
ou un temporisateur de dispatch.
Mais si vous avez vraiment besoin d'un délai dans le fil actuel:
... {
sleep(4)
}
Ceci utilise la fonction sleep
de UNIX.
L'utilisation d'un bloc dispatch_after
est dans la plupart des cas préférable à l'utilisation de sleep(time)
car le thread sur lequel la mise en veille est effectuée est empêché d'effectuer un autre travail. Lorsque vous utilisez dispatch_after
, le thread sur lequel vous travaillez n'est pas bloqué, il peut donc effectuer un autre travail entre-temps.
Si vous travaillez sur le fil principal de votre application, utiliser sleep(time)
est mauvais pour l'expérience utilisateur de votre application, car l'interface utilisateur ne répond pas pendant cette période.
Dispatch après la planification de l'exécution d'un bloc de code au lieu de geler le thread:
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
// 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
}
Comparaison entre différentes approches dans Swift 3.0
1. Sommeil
Cette méthode n'a pas de rappel. Mettez les codes directement après cette ligne à exécuter en 4 secondes. Cela empêchera l'utilisateur d'itérer avec des éléments de l'interface utilisateur comme le bouton de test jusqu'à ce que le temps soit écoulé. Bien que le bouton soit en quelque sorte gelé lorsque le sommeil s'active, d'autres éléments, tels que l'indicateur d'activité, continuent de tourner sans geler. Vous ne pouvez plus déclencher cette action pendant le sommeil.
sleep(4)
print("done")//Do stuff here
2. Dispatch, Perform et Timer
Ces trois méthodes fonctionnent de la même manière, elles s'exécutent toutes sur le thread d'arrière-plan avec des rappels, avec une syntaxe différente et des fonctionnalités légèrement différentes.
Dispatch est couramment utilisé pour exécuter quelque chose sur le thread d'arrière-plan. Il a le rappel dans le cadre de l'appel de fonction
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
print("done")
})
Effectuer est en réalité une minuterie simplifiée. Il configure une minuterie avec le délai, puis déclenche la fonction par sélecteur.
perform(#selector(callback), with: nil, afterDelay: 4.0)
func callback() {
print("done")
}}
Enfin, le minuteur permet également de répéter le rappel, ce qui n’est pas utile dans ce cas.
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)
func callback() {
print("done")
}}
Pour ces trois méthodes, lorsque vous cliquez sur le bouton pour les déclencher, l'interface utilisateur ne se fige pas et vous êtes autorisé à cliquer à nouveau dessus. Si vous cliquez à nouveau sur le bouton, une autre minuterie est configurée et le rappel est déclenché deux fois.
En conclusion
Aucune des quatre méthodes ne fonctionne assez bien par elles-mêmes. sleep
désactivera l’interaction de l’utilisateur, ainsi l’écran " gèle " (pas réellement) et entraîne une mauvaise expérience utilisateur. Les trois autres méthodes ne gèlent pas l'écran, mais vous pouvez les déclencher plusieurs fois. La plupart du temps, vous souhaitez attendre que l'appel soit retourné avant de permettre à l'utilisateur de le rappeler.
Une meilleure conception utilisera donc l’une des trois méthodes asynchrones avec blocage d’écran. Lorsque l'utilisateur clique sur le bouton, couvrez la totalité de l'écran avec une vue translucide surmontée d'un indicateur d'activité en rotation, indiquant à l'utilisateur que le clic du bouton est en cours de traitement. Supprimez ensuite la vue et l'indicateur dans la fonction de rappel, indiquant à l'utilisateur que l'action est correctement gérée, etc.
Je suis d’accord avec Palle pour dire que dispatch_after
est un bon choix ici. Mais vous n'aimez probablement pas les appels GCD car ils sont assez gênants pour écrire . Au lieu de cela, vous pouvez ajouter cette aide pratique :
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)
}
}
}
Maintenant, vous simplement retardez votre code sur un thread d'arrière-plan comme ceci:
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Retarder le code sur le thread principal est encore plus simple:
delay(bySeconds: 1.5) {
// delayed code, by default run in main thread
}
Si vous préférez un Framework qui présente également des fonctionnalités plus pratiques, cliquez sur checkout HandySwift. Vous pouvez l'ajouter à votre projet via Carthage ou Accio puis utilisez-le exactement comme dans le exemples ci-dessus:
import HandySwift
delay(by: .seconds(1.5)) {
// delayed code
}
Vous pouvez également le faire avec Swift 3.
Exécutez la fonction après le délai comme suit.
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")
}
La réponse de @nneonneo a suggéré d'utiliser NSTimer
mais n'a pas montré comment le faire. C'est la syntaxe de base:
let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)
Voici un projet très simple pour montrer comment il pourrait être utilisé. Lorsque vous appuyez sur un bouton, une minuterie appelle une fonction après un délai d’une demi-seconde.
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")
}
}
Utiliser dispatch_time
(comme dans réponse de Palle ) est une autre option valide. Cependant, il est difficile à annuler . Avec NSTimer
name__, pour annuler un événement retardé avant qu'il ne se produise, il vous suffit d'appeler
timer.invalidate()
L'utilisation de sleep
n'est pas recommandée, en particulier sur le thread principal, car elle arrête tout le travail effectué sur le thread.
Voir ici pour ma réponse plus complète.
Dans Swift 4.2 et Xcode 10.1
Vous avez totalement 4 façons de retarder. Hors de tout option 1 est préférable d'appeler ou d'exécuter une fonction après un certain temps. Hors de tout sleep () est le moins de cas utilisé.
Option 1.
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.yourFuncHere()
}
//Your function here
func yourFuncHere() {
}
Option 2.
perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)
//Your function here
@objc func yourFuncHere2() {
print("this is...")
}
Option 3.
Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)
//Your function here
@objc func yourFuncHere3() {
}
Option 4.
sleep(5)
Essayez l'implémentation suivante dans Swift 3.0
func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
Usage
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
}
}
Si vous devez définir un délai inférieur à une seconde, il n'est pas nécessaire de définir le paramètre .seconds. J'espère que cela est utile à quelqu'un.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
// your code hear
})
Pour créer un délai simple, vous pouvez importer Darwin, puis utiliser le délai d’attente (secondes). Cela ne prend toutefois que quelques secondes. Pour des mesures plus précises, vous pouvez importer Darwin et utiliser usleep (un millionième de seconde) pour des mesures très précises. Pour tester cela, j'ai écrit:
import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")
Qui imprime, puis attend 1 seconde et imprime, puis attend pendant 0.4 seconde puis imprime. Tout a fonctionné comme prévu.
Vous pouvez créer une extension pour utiliser facilement la fonction de délai (Syntaxe: 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)
}
}
Comment utiliser dans UIViewController
self.delay(0.1, closure: {
//execute code
})
Si votre code est déjà en cours d'exécution dans un thread en arrière-plan, mettez le thread en pause à l'aide de cette méthode dans Foundation : Thread.sleep(forTimeInterval:)
Par exemple:
DispatchQueue.global(qos: .userInitiated).async {
// Code is running in a background thread already so it is safe to sleep
Thread.sleep(forTimeInterval: 4.0)
}
(Voir les autres réponses pour des suggestions lorsque votre code est en cours d'exécution sur le thread principal.)