web-dev-qa-db-fra.com

Utiliser un NSTimer dans Swift

Dans ce scénario, timerFunc () n'est jamais appelé. Qu'est-ce que je rate?

class AppDelegate: NSObject, NSApplicationDelegate {

    var myTimer: NSTimer? = nil

    func timerFunc() {
        println("timerFunc()")
    }

    func applicationDidFinishLaunching(aNotification: NSNotification?) {
        myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
    }
}
42
RobertJoseph

Vous pouvez créer un minuteur programmé qui s’ajoute automatiquement au runloop et commence à se déclencher:

Swift 2

NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "timerDidFire:", userInfo: userInfo, repeats: true)

Swift 3, 4, 5

Timer.scheduledTimer(withTimeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: userInfo, repeats: true)

Ou bien, vous pouvez conserver votre code actuel et ajouter la minuterie au runloop lorsque vous êtes prêt pour cela:

Swift 2

let myTimer = NSTimer(timeInterval: 0.5, target: self, selector: "timerDidFire:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(myTimer, forMode: NSRunLoopCommonModes)

Swift 3, 4, 5

let myTimer = Timer(timeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(myTimer, forMode: RunLoop.Mode.common)
92
Ryan

j'utilise une approche similaire à Luke. Seulement une mise en garde pour les gens qui sont des puristes "méthodes privées":

NE PAS rendre le rappel privé dans Swift.

Si vous écrivez:

private func timerCallBack(timer: NSTimer){

..

tu auras:

timerCallBack:]: sélecteur non reconnu envoyé à l'instance ... Application en cours de fermeture en raison d'une exception non interceptée 'NSInvalidArgumentException'

8
ingconti

Les NSTimer ne sont pas programmés automatiquement, sauf si vous utilisez NSTimer.scheduledTimerWithTimeInterval:

myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc", userInfo: nil, repeats: true)
7
drewag

Comme Drewag et Ryan l'ont souligné, vous devez créer un chronomètre programmé (ou le programmer vous-même). Le plus simple est de le créer déjà programmé avec:

myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc:", userInfo: nil, repeats: true)

Vous devez également modifier votre définition de timerFunc (et le sélecteur associé) pour prendre un argument et se terminer par un ':'

func timerFunc(timer:NSTimer!) {
    ...
}
6
David Berry

Pour Swift

var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.updateTimer), userInfo: nil, repeats: true);
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
6

Syntaxe Swift 3.0 pour la chose en boucle d’exécution:

RunLoop.current.add(myTimer, forMode: .commonModes)
5
Florin Odagiu

C'est un peu de code, qui montre comment appeler une fonction (en différé) avec AND sans paramètre.

Utilisez ceci dans un nouveau projet en xCode (singleViewApplication) et placez le code dans le viewController standard:

class ViewController: UIViewController {

    override func viewDidLoad() {

        super.viewDidLoad()

        NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("delayedFunctionWithoutParameter:"), userInfo: nil, repeats: false)

        let myParameter = "ParameterStringOrAnyOtherObject"

        NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: Selector("delayedFunctionWithParameter:"), userInfo: myParameter, repeats: false)
    }

    // SIMPLE TIMER - Delayed Function Call
    func delayedFunctionWithoutParameter(timer : NSTimer) {
        print("This is a simple function beeing called without a parameter passed")
        timer.invalidate()
    }

    // ADVANCED TIMER - Delayed Function Call with a Parameter
    func delayedFunctionWithParameter(timer : NSTimer) {

        // check, wether a valid Object did come over
        if let myUserInfo: AnyObject = timer.userInfo {
            // alternatively, assuming it is a String for sure coming over
            // if let myUserInfo: String = timer.userInfo as? String {
            // assuming it is a string comming over
            print("This is an advanced function beeing called with a parameter (in this case: \(myUserInfo)) passed")
        }

        timer.invalidate()
    }
}

Notez que dans tous les cas, vous devez implémenter la fonction différée avec le paramètre (timer: NSTimer) pour pouvoir invalider (terminer, terminer) le timer. Et avec le "timer" de passe, vous avez également accès à userInfo (et vous pouvez y placer n'importe quel objet, pas seulement String-Objects, ainsi que des types de collection tels que des tableaux et des dictionnaires).

Les documentations d'origine Apple indiquent "" -> Le temporisateur se passe lui-même comme argument. La méthode adoptera donc le modèle suivant: void) timerFireMethod: (NSTimer *) timer Lecture complète -> --- (ici

4
LukeSideWalker

Comme ce fil de discussion m'a fait essayer de mettre le minuteur sur un RunLoop moi-même (ce qui a résolu mon problème), je poste aussi mon cas spécifique - qui sait peut-être que cela peut aider quelqu'un. Mon minuteur est créé lors du démarrage de l'application et de l'initialisation de tous les objets. Mon problème était que, bien qu'il ait programmé le chronomètre, il ne s'est toujours pas déclenché. J'imagine que c'était le cas parce que scheduledTimerWithTimeInterval plaçait le minuteur sur un autre RunLoop lors du démarrage de l'application. Si je viens d'initialiser le chronomètre et d'utiliser ensuite NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode), cela fonctionne correctement.

3
everSin berserch

Avec Swift3, vous pouvez le lancer avec,

var timer: Timer?
func startTimer() {

    if timer == nil {
        timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.loop), userInfo: nil, repeats: true)
    }
}

func stopTimer() {
    if timer != nil {
        timer?.invalidate()
        timer = nil
    }
}

func loop() {
    //do something
}
3
Kris Roofe

Pour le faire avec la méthode suggérée par l'OP, vous devez l'ajouter à une boucle d'exécution:

myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode)

La documentation indique également que la cible doit accepter un argument, mais que cela ne fonctionne pas.

func timerFireMethod(timer: NSTimer) { }
2
Grimxn