Swift - Timer
Links[Bearbeiten]
Swift - Snippets
Timer Klasse[Bearbeiten]
Timer.scheduledTimer[Bearbeiten]
In Swift ist die Timer.scheduledTimer-Funktion eine Methode, die einen Timer erstellt und ihn automatisch startet. Die Funktion nimmt mehrere Parameter an, darunter:
timeInterval: Das Intervall, in dem der Timer ausgelöst werden soll (in Sekunden). repeats: Ein Bool-Wert, der angibt, ob der Timer wiederholt werden soll oder nicht. block: Ein Closure (eine Art von Funktion), das ausgeführt wird, wenn der Timer ausgelöst wird.
Die Timer.scheduledTimer-Funktion gibt ein Timer-Objekt zurück, das verwendet werden kann, um den Timer zu kontrollieren. Du kannst beispielsweise das Timer-Objekt verwenden, um den Timer zu invalidieren (beenden) oder um das Intervall des Timers zu ändern.
Hier ist ein Beispiel, wie man die Timer.scheduledTimer-Funktion verwenden könnte, um einen Timer zu erstellen, der alle 2 Sekunden ausgelöst wird und eine Meldung ausgibt:
import Foundation
let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { timer in
print("Der Timer wurde ausgelöst!")
}
Hinweis: Die Timer.scheduledTimer-Funktion ist eine Funktion von Foundation, dem Framework für grundlegende Funktionen in Swift. Du musst daher zunächst import Foundation schreiben, um die Funktion verwenden zu können.
Beispiele[Bearbeiten]
Beispiel 1[Bearbeiten]
import Foundation
// Setze den Timer auf 5 Minuten (in Sekunden)
let timerDuration: TimeInterval = 300
// Erstelle den Timer
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
// Verringere die Dauer des Timers um 1 Sekunde
timerDuration -= 1
// Wenn der Timer abgelaufen ist, beende ihn
if timerDuration <= 0 {
timer.invalidate()
}
// Ansonsten, gib den verbleibenden Zeitraum aus
else {
print(timerDuration)
}
}
Beispiel 2[Bearbeiten]
import Foundation
class CountdownTimer {
var counter = 300 // Startwert für den Zähler (300 Sekunden entspricht 5 Minuten)
var timer: Timer? // Der Timer selbst
func start() {
// Erstelle einen Timer, der jede Sekunde ausgelöst wird
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
}
@objc func updateCounter() {
counter -= 1 // Verringere den Zähler um 1
if counter == 0 {
timer?.invalidate() // Stoppe den Timer, wenn der Zähler 0 erreicht hat
}
}
}
let countdownTimer = CountdownTimer()
Timer mit Startzeitpunkt[Bearbeiten]
There are a few issues here:
Every time onTimerFires is called, it is decrementing timeHours (which does not appear to be set or used anywhere) and then calls timeFunc, which just grabs values from the picker (which are not changing anywhere) to build the string.
This pattern of decrementing a counter in a timer makes a big assumption, namely that the timer is invoked exactly at the right time and that it will not ever miss a timer call. That is not a prudent assumption, unfortunately.
I would advise against the Selector based timer, as that introduces a strong reference cycle with the target. The block-based rendition with weak references is easier to avoid these sorts of cycles.
I would suggest a different pattern, namely that you save the time to which you are counting down:
var countDownDate: Date!
func updateCountDownDate() {
let components = DateComponents(hour: pickerView.selectedRow(inComponent: 0),
minute: pickerView.selectedRow(inComponent: 1),
second: pickerView.selectedRow(inComponent: 2))
countDownDate = Calendar.current.date(byAdding: components, to: Date())
}
Then your timer handler can calculate the amount of elapsed time to the target count down date/time:
func handleTimer(_ timer: Timer) {
let remaining = countDownDate.timeIntervalSince(Date())
guard remaining >= 0 else {
timer.invalidate()
return
}
label.text = timeString(from: remaining)
}
When you start the timer, you calculate the countDownDate and schedule the timer:
weak var timer: Timer?
func startTimer() {
timer?.invalidate() // if one was already running, cancel it
updateCountDownDate()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
self.handleTimer(timer)
}
timer?.fire()
}
And, for what is worth, when preparing the string, you certainly can determine the date components between to dates (i.e., now and your target date/time), but you can also use a DateComponentsFormatter do this for you:
let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
func timeString(from interval: TimeInterval) -> String? {
return formatter.string(from: interval)
}
countdownTimer.start()