Swift - Timer

Aus Wikizone
Wechseln zu: Navigation, Suche

Links

Swift - Snippets

Beispiele

Beispiel 1

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

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

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