Comment puis-je utiliser Timer (anciennement NSTimer) dans Swift?

257

j'ai essayé

var timer = NSTimer()
timer(timeInterval: 0.01, target: self, selector: update, userInfo: nil, repeats: false)

Mais j'ai une erreur en disant

'(timeInterval: $T1, target: ViewController, selector: () -> (), userInfo: NilType, repeats: Bool) -> $T6' is not identical to 'NSTimer'
user3225917
la source
1
"Comment puis-je utiliser NSTimer dans Swift?" - de la même manière que vous l'utilisez dans Objective-C. Son API n'a pas changé.
The Paramagnetic Croissant

Réponses:

535

Cela fonctionnera:

override func viewDidLoad() {
    super.viewDidLoad()
    // Swift block syntax (iOS 10+)
    let timer = Timer(timeInterval: 0.4, repeats: true) { _ in print("Done!") }
    // Swift >=3 selector syntax
    let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
    // Swift 2.2 selector syntax
    let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: #selector(MyClass.update), userInfo: nil, repeats: true)
    // Swift <2.2 selector syntax
    let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}

// must be internal or public. 
@objc func update() {
    // Something cool
}

Pour Swift 4, la méthode dont vous voulez obtenir le sélecteur doit être exposée à Objective-C, donc l' @objcattribut doit être ajouté à la déclaration de méthode.

Oscar Swanros
la source
2
J'ajouterais que la classe avec ces méthodes doit être un NSObject, sinon vous vous retrouvez avec une erreur de sélection non reconnue
Joshua
27
Depuis Xcode 6.1, je devais ajouter "@objc" à l'en-tête de la fonction comme ceci: "@objc func update () {". Sans elle, l'application se bloque au premier incendie.
kev
Vous pouvez déclarer Var timer: NSTimer! au départ et utilisez-le quand vous en avez besoin!
Nigilan
1
Une version peut-être plus utile de la syntaxe du bloc: let timer = Timer.scheduledTimer (withTimeInterval: timeout, repeats: false) {_ in print ("Done.")}
Teo Sartori
Vous ne pouvez pas utiliser 'let timer = Timer (timeInterval: 0.4, repeats: true) {_ in print ("Done!")}' Ceci ne démarrera pas le timer et vous ne pourrez pas le faire répéter. Vous devez utiliser Timer.scheduledTimer.
Siamaster
149

Événement répété

Vous pouvez utiliser une minuterie pour effectuer une action plusieurs fois, comme illustré dans l'exemple suivant. Le minuteur appelle une méthode pour mettre à jour une étiquette toutes les demi-secondes.

entrez la description de l'image ici

Voici le code pour cela:

import UIKit

class ViewController: UIViewController {

    var counter = 0
    var timer = Timer()

    @IBOutlet weak var label: UILabel!

    // start timer
    @IBAction func startTimerButtonTapped(sender: UIButton) {
        timer.invalidate() // just in case this button is tapped multiple times

        // start the timer
        timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
    }

    // stop timer
    @IBAction func cancelTimerButtonTapped(sender: UIButton) {
        timer.invalidate()
    }

    // called every time interval from the timer
    func timerAction() {
        counter += 1
        label.text = "\(counter)"
    }
}

Événement retardé

Vous pouvez également utiliser une minuterie pour planifier un événement unique pendant un certain temps dans le futur. La principale différence avec l'exemple ci-dessus est que vous utilisez repeats: falseau lieu de true.

timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)

L'exemple ci-dessus appelle une méthode nommée delayedActiondeux secondes après le réglage du temporisateur. Il n'est pas répété, mais vous pouvez toujours appeler timer.invalidate()si vous devez annuler l'événement avant qu'il ne se produise.

Remarques

  • S'il y a une chance de démarrer plusieurs fois votre instance de minuteur, assurez-vous d'invalider d'abord l'ancienne instance de minuteur. Sinon, vous perdez la référence à la minuterie et vous ne pouvez plus l'arrêter. (voir cette Q&R )
  • N'utilisez pas de minuteries lorsqu'elles ne sont pas nécessaires. Consultez la section des minuteries du Guide d'efficacité énergétique pour les applications iOS .

en relation

Suragch
la source
1
@raddevus, merci de me le faire savoir. J'ai supprimé l'ancien commentaire de Swift 3.
Suragch
31

Mise à jour vers Swift 4, exploitant userInfo:

class TimerSample {

    var timer: Timer?

    func startTimer() {
        timer = Timer.scheduledTimer(timeInterval: 5.0,
                                     target: self,
                                     selector: #selector(eventWith(timer:)),
                                     userInfo: [ "foo" : "bar" ],
                                     repeats: true)
    }

    // Timer expects @objc selector
    @objc func eventWith(timer: Timer!) {
        let info = timer.userInfo as Any
        print(info)
    }

}
igraczech
la source
2
Montrez un exemple de travail, qu'est-ce que "personnalisé" et "données" signifient si la fonction attend un NSTimerobjet
Carlos.V
1
Cela n'a vraiment pas d'importance. Vous êtes libre de stocker tout ce dont vous avez besoin dans le dictionnaire userInfo, dans ce cas, il s'agit d'une paire clé-valeur arbitraire.
igraczech
C'est utile, mais cassé dans Swift 3, exemple de travail: Timer.scheduledTimer (timeInterval: 1.0, cible: self, selector: #selector (event), userInfo: "Info Sent", répète: vrai)
Bobby
28

Depuis iOS 10, il existe également une nouvelle méthode d'usine de minuterie basée sur des blocs qui est plus propre que l'utilisation du sélecteur:

    _ = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
        label.isHidden = true
    }
Josh Homann
la source
1
La façon dont vous le faites, ne serait-il pas préférable de simplement supprimer le _ = et de commencer Timer?
Honey
2
Vous pouvez omettre le _ = si vous désactivez l'avertissement concernant la valeur inutilisée ou si vous ne vous souciez pas des avertissements. Je n'aime pas enregistrer le code avec des avertissements.
Josh Homann
22

Swift 3, pré iOS 10

func schedule() {
    DispatchQueue.main.async {
      self.timer = Timer.scheduledTimer(timeInterval: 20, target: self,
                                   selector: #selector(self.timerDidFire(timer:)), userInfo: nil, repeats: false)
    }
  }

  @objc private func timerDidFire(timer: Timer) {
    print(timer)
  }

Swift 3, iOS 10+

DispatchQueue.main.async {
      self.timer = Timer.scheduledTimer(withTimeInterval: 20, repeats: false) { timer in
        print(timer)
      }
    }

Remarques

  • Il doit être dans la file d'attente principale
  • La fonction de rappel peut être publique, privée, ...
  • La fonction de rappel doit être @objc
onmyway133
la source
1
Ma compréhension est que seul le rappel du minuteur doit être dans la file d'attente principale et que les éléments suivants seraient légèrement plus efficaces: self.timer = Timer.scheduledTimer (withTimeInterval: 20, repeats: false) {timer in DispatchQueue.main.async {print (timer)}}
Mathieu Frenette
Ma minuterie ne se déclenchait pas à partir d'un de mes objets et cela a fait l'affaire :)
Reimond Hill
@ReimondHill Vous devez changertimeInterval
onmyway133
17

Vérifier avec:

Swift 2

var timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("update"), userInfo: nil, repeats: true)

Swift 3, 4, 5

var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
Midhun MP
la source
2
Je l'ai déjà essayé mais il dit 'Impossible de trouver une surcharge pour' init 'qui accepte les arguments fournis'
user3225917
1
De même ici, j'ai eu l'erreur «Impossible de trouver une surcharge pour« init »qui accepte les arguments fournis». Est-ce que cette ligne fonctionne vraiment?
Yangshun Tay
J'obtiens la même erreur que @yangshun. Quel type d'objet doit selfêtre? UIView est ok?
SimplGy
@SimpleAsCouldBe: oui c'est ok
Midhun MP
func amountSubmitSuccess () {self.view.hideToastActivity () self.view.makeToast (message: "The Amount Successfully Registered") var timer = NSTimer.scheduledTimerWithTimeInterval (0.5, cible: self, sélecteur: "moveToBidderPage", userInfo: nil, répète: faux)} func moveToBidderPage () {let loginPageView = self.storyboard? .instantiateViewControllerWithIdentifier ("bidderpageID") as! BidderPage self.navigationController? .PushViewController (loginPageView, animé: true)}
AG
11

Vous devrez utiliser Timer au lieu de NSTimer dans Swift 3.

Voici un exemple:

Timer.scheduledTimer(timeInterval: 1, 
    target: self, 
    selector: #selector(YourController.update), 
    userInfo: nil, 
    repeats: true)

// @objc selector expected for Timer
@objc func update() {
    // do what should happen when timer triggers an event
}
Ondrej Kvasnovsky
la source
11

Swift 5

Personnellement, je préfère la minuterie avec fermeture de bloc:

    Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
       // TODO: - whatever you want
    }
Wissa
la source
Attention, ceci n'est disponible que dans macOS 10.12 ou plus récent. Je ne suis pas sûr d'ios.
jeff-h
Il est également disponible sur iOS.
Wissa
7

pour swift 3 et Xcode 8.2 (agréable d'avoir des blocs, mais si vous compilez pour iOS9 ET voulez userInfo):

...

        self.timer = Timer(fireAt: fire,
                           interval: deltaT,
                           target: self,
                           selector: #selector(timerCallBack(timer:)),
                           userInfo: ["custom":"data"],
                           repeats: true)

        RunLoop.main.add(self.timer!, forMode: RunLoopMode.commonModes)
        self.timer!.fire()
}

func timerCallBack(timer: Timer!){
        let info = timer.userInfo
        print(info)
    }
ingconti
la source
6

SimpleTimer (Swift 3.1)

Pourquoi?

Il s'agit d'une classe de minuteur simple en swift qui vous permet de:

  • Minuterie locale
  • Chaînable
  • Une doublure
  • Utiliser des rappels réguliers

Usage:

SimpleTimer(interval: 3,repeats: true){print("tick")}.start()//Ticks every 3 secs

Code:

class SimpleTimer {/*<--was named Timer, but since swift 3, NSTimer is now Timer*/
    typealias Tick = ()->Void
    var timer:Timer?
    var interval:TimeInterval /*in seconds*/
    var repeats:Bool
    var tick:Tick

    init( interval:TimeInterval, repeats:Bool = false, onTick:@escaping Tick){
        self.interval = interval
        self.repeats = repeats
        self.tick = onTick
    }
    func start(){
        timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(update), userInfo: nil, repeats: true)//swift 3 upgrade
    }
    func stop(){
        if(timer != nil){timer!.invalidate()}
    }
    /**
     * This method must be in the public or scope
     */
    @objc func update() {
        tick()
    }
}
éoniste
la source
Comment puis arrêter le chronomètre à l'intérieur de ce bloc sous certaines conditions?
Développeur mobile iOS Android
Il suffit de stocker la référence à la minuterie dans une classe, puis d'appeler simplement stop. Le compilateur xcode vous dira s'il doit s'échapper, etc.
eonist
3
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)

Et créez du plaisir par le nom createEnemy

fund createEnemy ()
{
do anything ////
}
Khaled Hamdy
la source
3

Déclarez d'abord votre chronomètre

var timer: Timer?

Ensuite, ajoutez la ligne dans viewDidLoad () ou dans n'importe quelle fonction que vous souhaitez démarrer la minuterie

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(action), userInfo: nil, repeats: false)

C'est le func que vous le rappellerez pour faire quelque chose qu'il doit être @objc

@objc func action () {
print("done")
}
génie
la source
2

Dans Swift 3, quelque chose comme ça avec @objc:

func startTimerForResendingCode() {
    let timerIntervalForResendingCode = TimeInterval(60)
    Timer.scheduledTimer(timeInterval: timerIntervalForResendingCode,
                         target: self,
                         selector: #selector(timerEndedUp),
                         userInfo: nil,
                         repeats: false)
}




@objc func timerEndedUp() {
    output?.timerHasFinishedAndCodeMayBeResended()
}
Nik Kov
la source
1

Si vous lancez la méthode de la minuterie

let timer = Timer(timeInterval: 3, target: self, selector: #selector(update(_:)), userInfo: [key : value], repeats: false)

func update(_ timer : Timer) {

}

puis ajoutez-le à la boucle en utilisant la méthode autre sélecteur ne sera pas appelé

RunLoop.main.add(timer!, forMode: .defaultRunLoopMode)

REMARQUE: Si vous souhaitez que cela se répète, effectuez des répétitions vraies et conservez la référence du minuteur, sinon la méthode de mise à jour ne sera pas appelée.

Si vous utilisez cette méthode.

Timer.scheduledTimer(timeInterval: seconds, target: self, selector: #selector(update(_:)), userInfo: nil, repeats: true)

conserver une référence pour une utilisation ultérieure si les répétitions sont vraies.

Surjeet Rajput
la source
0

J'ai essayé de faire dans une classe NSObject et cela a fonctionné pour moi:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {  
print("Bang!") }
Álvaro Agüero
la source
-2

NSTimer a été renommé Timer dans Swift 4.2. cette syntaxe fonctionnera en 4.2:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)
Jamtrax Reunion
la source
Le changement de nom s'est produit dans Swift 3 et d'autres réponses ont déjà fait la mise à jour ...
Eric Aya