web-dev-qa-db-fra.com

Comment créer un délai dans Swift?

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.

211
Schuey999

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.

220
nneonneo

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:

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
}
295
Palle

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

enter image description 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.

enter image description here

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.

38
Fangming

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
}
30
Dschee

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")
        }
24
Cyril Ivar Garcia

NSTimer

La réponse de @nneonneo a suggéré d'utiliser NSTimermais 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 NSTimername__, pour annuler un événement retardé avant qu'il ne se produise, il vous suffit d'appeler

timer.invalidate()

L'utilisation de sleepn'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.

18
Suragch

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

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
}
6
Vakas
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
    }
}
5
eonist

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

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.

2
Ethan Wrightson

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
})
1
Hardik Thakkar

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

0
Robin Stewart