¿Cómo puedo usar Timer (anteriormente NSTimer) en Swift?

257

Lo intenté

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

Pero recibí un error diciendo

'(timeInterval: $T1, target: ViewController, selector: () -> (), userInfo: NilType, repeats: Bool) -> $T6' is not identical to 'NSTimer'
usuario3225917
fuente
1
"¿Cómo puedo usar NSTimer en Swift?" - De la misma manera que lo usa en Objective-C. Su API no cambió.
El cruasán paramagnético

Respuestas:

535

Esto funcionará:

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
}

Para Swift 4, el método del que desea obtener el selector debe estar expuesto a Objective-C, por lo tanto, el @objcatributo debe agregarse a la declaración del método.

Oscar Swanros
fuente
2
Agregaría que la clase con estos métodos debe ser un NSObject, de lo contrario terminará con un error de selector no reconocido
Joshua
27
A partir de Xcode 6.1, tuve que agregar "@objc" al encabezado de la función de esta manera: "@objc func update () {". Sin ella, la aplicación se bloquea en el primer incendio.
kev
Puedes declarar Var timer: NSTimer! inicialmente y úselo cuando sea necesario!
Nigilan
1
Una versión quizás más útil de la sintaxis de bloque: let timer = Timer.scheduledTimer (withTimeInterval: timeout, repits: false) {_ in print ("Done.")}
Teo Sartori
No puede usar 'let timer = Timer (timeInterval: 0.4, repeats: true) {_ in print ("Done!")}' Esto no iniciará el temporizador y no podrá hacer que se repita. Debe usar Timer.scheduledTimer.
Siamaster
149

Evento repetido

Puede usar un temporizador para realizar una acción varias veces, como se ve en el siguiente ejemplo. El temporizador llama a un método para actualizar una etiqueta cada medio segundo.

ingrese la descripción de la imagen aquí

Aquí está el código para eso:

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 retrasado

También puede usar un temporizador para programar un evento único durante algún tiempo en el futuro. La principal diferencia con el ejemplo anterior es que usa en repeats: falselugar de true.

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

El ejemplo anterior llama a un método llamado delayedActiondos segundos después de que se establece el temporizador. No se repite, pero aún puede llamartimer.invalidate() si necesita cancelar el evento antes de que ocurra.

Notas

  • Si existe alguna posibilidad de iniciar su instancia del temporizador varias veces, asegúrese de invalidar primero la instancia del temporizador anterior. De lo contrario, perderá la referencia al temporizador y ya no podrá detenerlo. (ver estas preguntas y respuestas )
  • No use temporizadores cuando no sean necesarios. Consulte la sección de temporizadores de la Guía de eficiencia energética para aplicaciones iOS .

Relacionado

Suragch
fuente
1
@raddevus, gracias por hacérmelo saber. Eliminé el viejo comentario de Swift 3.
Suragch
31

Actualizado a Swift 4, aprovechando 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
fuente
2
Muestre un ejemplo de trabajo, qué significa "personalizado" y "datos" si la función espera un NSTimerobjeto
Carlos.V
1
Realmente no importa. Puede almacenar cualquier cosa que necesite en el diccionario userInfo, en este caso es un par arbitrario de clave-valor.
igraczech
Esto es útil, pero se rompió en Swift 3, ejemplo de trabajo: Timer.scheduledTimer (timeInterval: 1.0, target: self, selector: #selector (event), userInfo: "Info Sent", repite: true)
Bobby
28

A partir de iOS 10, también hay un nuevo método de fábrica de temporizador basado en bloques que es más limpio que usar el selector:

    _ = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
        label.isHidden = true
    }
Josh Homann
fuente
1
De la forma en que lo estás haciendo, ¿no sería mejor eliminarlo _ = y comenzar Timer?
Miel
2
Puede omitir _ = si silencia la advertencia sobre el valor no utilizado o si simplemente no le importan las advertencias. No me gusta verificar el código con advertencias.
Josh Homann
22

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

Notas

  • Tiene que estar en la cola principal.
  • La función de devolución de llamada puede ser pública, privada, ...
  • La función de devolución de llamada debe ser @objc
onmyway133
fuente
1
Entiendo que solo la devolución de llamada del temporizador debe estar en la cola principal y que lo siguiente sería un poco más eficiente: self.timer = Timer.scheduledTimer (withTimeInterval: 20, repite: false) {timer en DispatchQueue.main.async {print (temporizador)}}
Mathieu Frenette
Mi temporizador no se desencadena a partir de una de mis objetos y que hizo el truco :)
Reimond Colina
@ReimondHill Necesitas cambiartimeInterval
onmyway133
17

Comprueba 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)
Midhun MP
fuente
2
Ya lo intenté pero dice 'No se pudo encontrar una sobrecarga para' init 'que acepte los argumentos proporcionados'
user3225917
1
Lo mismo aquí, recibí el error 'No se pudo encontrar una sobrecarga para' init 'que acepta los argumentos proporcionados'. ¿Esta línea realmente funciona?
Yangshun Tay
Me sale el mismo error que @yangshun. ¿Qué tipo de objeto debe selfser? UIView está bien?
SimplGy
@SimpleAsCouldBe: sí, eso está bien
Midhun MP
func amountSubmitSuccess () {self.view.hideToastActivity () self.view.makeToast (mensaje: "The Amount Successful Registered") var timer = NSTimer.scheduledTimerWithTimeInterval (0.5, target: self, selector: "moveToBidderPage", userInfo: nil, repite: false)} func moveToBidderPage () {let loginPageView = self.storyboard? .instantiateViewControllerWithIdentifier ("bidderpageID") como! BidderPage self.navigationController? .PushViewController (loginPageView, animado: verdadero)}
AG
11

Deberá usar el temporizador en lugar del NSTimer en Swift 3.

Aquí hay un ejemplo:

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
fuente
11

Swift 5

Personalmente prefiero el temporizador con el cierre de bloque:

    Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
       // TODO: - whatever you want
    }
Wissa
fuente
Tenga en cuenta que esto solo está disponible en macOS 10.12 o posterior. No estoy seguro acerca de ios.
jeff-h
También está disponible en iOS.
Wissa
7

para swift 3 y Xcode 8.2 (es bueno tener bloques, pero si compila para iOS9 Y desea información de usuario):

...

        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
fuente
6

Temporizador simple (Swift 3.1)

¿Por qué?

Esta es una clase de temporizador simple en Swift que le permite:

  • Temporizador de alcance local
  • Encadenable
  • Uno liners
  • Use devoluciones de llamada regulares

Uso:

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

Código:

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()
    }
}
eonista
fuente
¿Cómo detener el temporizador dentro de ese bloque en algunas condiciones?
Desarrollador móvil iOS Android
Simplemente almacene la referencia al temporizador en una clase y luego simplemente llame a stop. El compilador Xcode le dirá si necesita escapar etc.
eonist
3
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)

Y crea diversión por el nombre createEnemy

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

Primero declara tu temporizador

var timer: Timer?

Luego agregue una línea en viewDidLoad () o en cualquier función que desee iniciar el temporizador

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

Esta es la función a la que devolverá la llamada para hacer algo que debe ser @objc

@objc func action () {
print("done")
}
genio
fuente
2

En Swift 3 algo así 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()
}
Nik Kov
fuente
1

Si inicia el método del temporizador

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

func update(_ timer : Timer) {

}

luego agréguelo al bucle usando el método que no se llamará a otro selector

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

NOTA: Si desea que esto se repita, haga que las repeticiones sean verdaderas y mantenga la referencia del temporizador; de lo contrario, no se llamará al método de actualización.

Si estás usando este método.

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

mantenga una referencia para su uso posterior si las repeticiones son verdaderas.

Surjeet Rajput
fuente
0

Traté de hacer en una clase NSObject y esto funcionó para mí:

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

NSTimer ha cambiado de nombre a Timer en Swift 4.2. esta sintaxis funcionará en 4.2:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)
Reunión Jamtrax
fuente
El cambio de nombre ocurrió en Swift 3 y otras respuestas ya han hecho la actualización ...
Eric Aya