2014-06-23 9 views
37

In questo scenario, timerFunc() non viene mai chiamato. Cosa mi manca?Utilizzo di un NSTimer in Swift

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

Se si utilizza il 'init' del timer è necessario' È necessario aggiungere il nuovo timer a un ciclo di esecuzione, utilizzando addTimer: forMode: '. È la seconda frase nella descrizione del documento. Altrimenti usa 'scheduledTimerWithTimeInterval' che probabilmente è quello che stavi cercando. – Firo

+0

Perché i voti negativi di più persone hanno fornito risposte diverse? – RobertJoseph

+1

Non posso dirlo con certezza, ma probabilmente è perché la risposta non è difficile da trovare, come ho sottolineato prima. È giusto nei documenti. Se dovessi fare clic su 'option' sul metodo, avresti trovato la soluzione entro 5 secondi e senza nemmeno dover lasciare Xcode. – Firo

risposta

82

è possibile creare un timer programmato che si aggiunge automaticamente alla runloop e inizia a sparare:

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

Oppure, puoi mantenere il codice corrente e aggiungere il timer al runloop quando sei pronto per farlo:

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

Come mettere in pausa e riprendere questo timer? –

+0

myTimer.invalidate() lo annulla. Oltre a ciò, NSTimer non supporta alcuna funzionalità di pausa/ripresa. Potresti immaginare una sottoclasse semplice che aggiunge questo supporto (1) annotando il tempo di avvio, (2) notando il tempo di pausa e (3) sul curriculum, eseguito per t2-t1 più corto del nostro timeInterval originale. – Ryan

+0

Sintassi Sift 3.0 per il ciclo di esecuzione thingy: RunLoop.current.add (myTimer, forMode: RunLoopMode.commonModes) –

7

NSTimer di non sono previste automaticamente a meno che non si utilizza NSTimer.scheduledTimerWithTimeInterval:

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

Come Drewag e Ryan ha sottolineato, è necessario creare un timer programmato (o programmare voi stessi) E 'più facile per crearlo in programma già con:

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

È inoltre necessario modificare la definizione di timerFunc (e il selettore associata) per prendere un argomento e termina con un ':'

func timerFunc(timer:NSTimer!) { 
    ... 
} 
+2

Non è necessario definirlo prendere una discussione. Va bene comunque. – drewag

+1

ok @drewag, mi sono appena ricordato che alcune discussioni su SO su di esso non funzionavano senza ':', quindi l'ho provato nel parco giochi. È necessario il ":" se si definisce (correttamente) il callback come argomento. "Correttamente" perché la documentazione di 'NSTimer' dice che il callback deve prendere un singolo argomento. "Il selettore dovrebbe avere la seguente firma: timerFireMethod: (compresi i due punti per indicare che il metodo accetta un argomento)." Sembra come se tu potessi andare via senza: o l'argomento, ma direi comunque che la mia risposta è corretta sulla base della documentazione :) –

+1

gli argomenti sono anche facoltativi in ​​IBActions e in qualsiasi altro posto che abbia mai definito un selettore (almeno con un singolo argomento). È solo la natura del runtime. Il fatto che abbia omesso l'argomento non ha nulla a che fare con il perché non viene chiamato. Nel peggiore dei casi, farebbe un'eccezione sul non trovare il selettore, non fallire silenziosamente. – drewag

2

per farlo con il metodo del PO suggerisce, è necessario aggiungere ad un ciclo di esecuzione:

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

La documentazione dice anche che l'obiettivo dovrebbe prendere una discussione, ma funziona senza di essa.

func timerFireMethod(timer: NSTimer) { } 
4

Questo è un po 'di codice, che dimostra come chiamare una funzione (ritardata) con AND senza un parametro.

Utilizzare questo in un nuovo progetto in Xcode (singleViewApplication) e inserire il codice nel viewController di serie:

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

avviso, che in ogni caso è necessario implementare la funzione in ritardo con il parametro (timer: NSTimer) per poter invalidare (terminare, terminare) il timer. E con il "timer" del passante hai anche accesso a userInfo (e lì puoi mettere qualsiasi Object, non solo String-Objects, come pure tipi di raccolta come array e dizionari).

originali Mele documentazioni dice "" -> Il timer si passa come argomento, quindi il metodo avrebbe adottato il seguente schema: - (void) timerFireMethod: (NSTimer *) timer leggere integralmente ->here

8

uso un approccio simile a Luke. Solo un avvertimento per le persone che sono puristi "metodi privati":

NON rendere la richiamata privata in Swift.

se si scrive:

private func timerCallBack(timer: NSTimer){ 

..

si otterrà:

timerCallBack:]: selettore non riconosciuto inviato ad esempio ...Terminazione dell'app a causa dell'eccezione non rilevata "NSInvalidArgumentException"

+0

Grazie mille per questo! –

+3

aggiungi semplicemente '' @ objc'' al metodo privato e funzionerà anche: '' @objc private func timerCallBack (timer: NSTimer) {'' – Lensflare

+0

@ La risposta suggerita di Lensflare è corretta, hai solo bisogno di '@ objc' decoratore di metodi. – kyleturner

3

Poiché questo thread mi ha fatto provare a mettere il timer su un RunLoop (che ha risolto il mio problema), pubblico anche il mio caso specifico - chissà che forse aiuti qualcuno. Il mio timer viene creato durante l'avvio e l'inizializzazione di tutti gli oggetti. Il mio problema era che, mentre programmava il timer, non funzionava ancora. La mia ipotesi è, questo era il caso perché scheduledTimerWithTimeInterval stava mettendo il timer su un RunLoop diverso durante l'avvio dell'App. Se ho appena inizializzato il timer e poi uso NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode), invece, funziona bene.

5

Swift 3.0 sintassi per il thingy ciclo corsa:

RunLoop.current.add(myTimer, forMode: .commonModes) 
2

Con swift3, è possibile eseguire con,

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

Per Swift 3

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

L'uso del metodo 'scheduledTimer()' aggiungerà automaticamente il timer al runloop - non è necessario aggiungerlo manualmente. –