Come posso utilizzare Timer (precedentemente NSTimer) in Swift?


261

Provai

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

Ma ho ricevuto un errore che diceva

'(timeInterval: $T1, target: ViewController, selector: () -> (), userInfo: NilType, repeats: Bool) -> $T6' is not identical to 'NSTimer'

1
"Come posso utilizzare NSTimer in Swift?" - nello stesso modo in cui lo usi in Objective-C. La sua API non è cambiata.
Il croissant paramagnetico il

Risposte:


540

Questo funzionerà:

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
}

Per Swift 4, il metodo di cui si desidera ottenere il selettore deve essere esposto a Objective-C, quindi l' @objcattributo deve essere aggiunto alla dichiarazione del metodo.


2
Aggiungerei che la classe con questi metodi deve essere un NSObject, altrimenti si finisce con un errore di selezione non riconosciuto
Joshua

27
A partire da Xcode 6.1, ho dovuto aggiungere "@objc" all'intestazione della funzione in questo modo: "@objc func update () {". Senza di essa l'app si blocca al primo fuoco.
kev

Puoi dichiarare Var timer: NSTimer! inizialmente e usalo quando necessario!
Nigilan

1
Una versione forse più utile della sintassi del blocco: let timer = Timer.scheduledTimer (withTimeInterval: timeout, repeats: false) {_ in print ("Done.")}
Teo Sartori

Non puoi usare 'let timer = Timer (timeInterval: 0.4, repeats: true) {_ in print ("Done!")}' Questo non avvierà il timer e quindi non puoi farlo ripetere. È necessario utilizzare Timer.scheduledTimer.
Siamaster

151

Evento ripetuto

È possibile utilizzare un timer per eseguire un'azione più volte, come illustrato nell'esempio seguente. Il timer chiama un metodo per aggiornare un'etichetta ogni mezzo secondo.

inserisci qui la descrizione dell'immagine

Ecco il codice per questo:

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)"
    }
}

Evento ritardato

Puoi anche utilizzare un timer per programmare un evento occasionale per un po 'di tempo in futuro. La differenza principale rispetto all'esempio precedente è che usi repeats: falseinvece di true.

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

L'esempio precedente chiama un metodo denominato delayedActiondue secondi dopo l'impostazione del timer. Non viene ripetuto, ma puoi comunque chiamare timer.invalidate()se devi annullare l'evento prima che accada.

Appunti

  • Se esiste la possibilità di avviare più volte l'istanza del timer, assicurarsi di invalidare prima l'istanza del vecchio timer. Altrimenti perdi il riferimento al timer e non puoi più fermarlo. (vedi questa domanda e risposta )
  • Non utilizzare timer quando non sono necessari. Vedere la sezione dei timer della Guida all'efficienza energetica per le app iOS .

Relazionato


1
@raddevus, grazie per avermelo informato. Ho rimosso il vecchio commento di Swift 3.
Suragch

31

Aggiornato a Swift 4, sfruttando le informazioni utente:

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

}

2
Mostra un esempio funzionante, cosa significano "personalizzato" e "dati" se la funzione si aspetta un NSTimeroggetto
Carlos.V

1
Non importa davvero. Sei libero di memorizzare tutto ciò di cui hai bisogno nel dizionario userInfo, in questo caso è una coppia chiave-valore arbitraria.
igraczech

Questo è utile, ma si è rotto in Swift 3, esempio funzionante: Timer.scheduledTimer (timeInterval: 1.0, target: self, selector: #selector (event), userInfo: "Info Sent", repeats: true)
Bobby

29

A partire da iOS 10 esiste anche un nuovo metodo di fabbrica Timer basato su blocchi che è più pulito rispetto all'utilizzo del selettore:

    _ = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
        label.isHidden = true
    }

1
Per come lo fai, non sarebbe meglio rimuovere semplicemente il _ = e iniziare con Timer?
Honey

2
Puoi omettere _ = se silenzia l'avviso sul valore inutilizzato o se semplicemente non ti interessa gli avvisi. Non mi piace controllare il codice con gli avvisi.
Josh Homann

23

Swift 3, pre 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)
      }
    }

Appunti

  • Deve essere nella coda principale
  • La funzione di richiamata può essere pubblica, privata, ...
  • La funzione di richiamata deve essere @objc

1
La mia comprensione è che solo il callback del timer dovrebbe essere sulla coda principale e che quanto segue sarebbe leggermente più efficiente: self.timer = Timer.scheduledTimer (withTimeInterval: 20, repeats: false) {timer in DispatchQueue.main.async {print (timer)}}
Mathieu Frenette

Il mio timer non si attivava da uno dei miei oggetti e questo ha funzionato :)
Reimond Hill

@ReimondHill Devi cambiaretimeInterval
onmyway133

17

Controllare con:

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)

2
L'ho già provato ma dice "Impossibile trovare un sovraccarico per" init "che accetta gli argomenti forniti"
user3225917

1
Lo stesso qui, ho ricevuto l'errore "Impossibile trovare un sovraccarico per" init "che accetta gli argomenti forniti". Questa linea funziona davvero?
Yangshun Tay

Ottengo lo stesso errore di @yangshun. Che tipo di oggetto deve selfessere? UIView va bene?
SimplGy

@SimpleAsCouldBe: sì, va bene
Midhun MP

func importoSubmitSuccess () {self.view.hideToastActivity () self.view.makeToast (messaggio: "The Amount Successfully Registered") var timer = NSTimer.scheduledTimerWithTimeInterval (0.5, target: self, selector: "moveToBidderPage", userInfo: nil, ripete: false)} func moveToBidderPage () {let loginPageView = self.storyboard? .instantiateViewControllerWithIdentifier ("bidderpageID") as! OfferentePage self.navigationController? .PushViewController (loginPageView, animated: true)}
AG

11

Dovrai usare Timer invece di NSTimer in Swift 3.

Ecco un esempio:

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
}

11

Swift 5

Personalmente preferisco il Timer con chiusura a blocco:

    Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
       // TODO: - whatever you want
    }

Tieni presente che è disponibile solo in macOS 10.12 o versioni successive. Non sono sicuro di iOS.
jeff-h

È disponibile anche in iOS.
Wissa

7

per swift 3 e Xcode 8.2 (bello avere blocchi, ma se compili per iOS9 e vuoi 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)
    }

6

SimpleTimer (Swift 3.1)

Perché?

Questa è una semplice classe timer in swift che ti consente di:

  • Timer con ambito locale
  • Chainable
  • Una riga
  • Usa callback regolari

Utilizzo:

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

Codice:

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()
    }
}

Come fermare quindi il timer all'interno di quel blocco in alcune condizioni?
Sviluppatore mobile iOS Android

Memorizza semplicemente il ref sul timer in una classe e chiama stop. Il compilatore xcode ti dirà se ha bisogno di fuga ecc.
eonista

5

Per prima cosa dichiara il tuo timer

var timer: Timer?

Quindi aggiungi una riga in viewDidLoad () o in qualsiasi funzione in cui desideri avviare il timer

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

Questa è la funzione che richiamerai per fare qualcosa che deve essere @objc

@objc func action () {
print("done")
}

3
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)

E crea divertimento con il nome createEnemy

fund createEnemy ()
{
do anything ////
}

2

In Swift 3 qualcosa del genere con @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()
}

1

Se si avvia il metodo di timer

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

func update(_ timer : Timer) {

}

quindi aggiungilo al ciclo usando il metodo che un altro selettore non verrà chiamato

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

NOTA: Se vuoi che questo si ripeta, fai ripetizioni vere e mantieni il riferimento del timer altrimenti il ​​metodo di aggiornamento non verrà chiamato.

Se stai usando questo metodo.

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

conservare un riferimento per un uso successivo se le ripetizioni sono vere.


0

Ho provato a farlo in una classe NSObject e questo ha funzionato per me:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {  
print("Bang!") }

-2

NSTimer è stato rinominato Timer in Swift 4.2. questa sintassi funzionerà in 4.2:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)

La ridenominazione è avvenuta in Swift 3 e altre risposte hanno già fatto l'aggiornamento ...
Eric Aya
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.