问题
Bear with me I picked up Swift and Xcode a week ago (Im creating an app to learn Swift during my holidays)
I created a timer using scheduledTimer to act as an stopwatch. The seconds and minutes are running fine but I can't get the hours to work. What am I doing wrong?
currentTime = 0
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.currentTime += 1
let hoursPortion = String(format: "%02d", CGFloat(self.currentTime) * 60)
let minutesPortion = String(format: "%02d", self.currentTime / 60)
let secondsPortion = String(format: "%02d", self.currentTime % 60)
//let tenPortion = String(format: "%d", self.currentTime * 10 % 10)
self.TimerDisplay.text = "\(hoursPortion):\(minutesPortion):\(secondsPortion)"
}
回答1:
As others have said, you shouldn't try to keep track of the time yourself. Let the system figure out how much time has elapsed. And, I'd be inclined to use DateComponentsFormatter to format the string for you, too:
@IBOutlet weak var elapsedLabel: UILabel!
weak var timer: Timer?
private var formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
private func startTimer() {
timer?.invalidate() // stop prior timer, if any
let startTime = Date()
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.elapsedLabel.text = self?.formatter.string(from: startTime, to: Date())
}
}
deinit {
timer?.invalidate()
}
BTW, it’s a subtle point, but please notice the use of [weak self] in the Timer closure’s capture list. In the absence of that, you have a variation of a strong reference cycle because timers don't stop until you invalidate them, but the view controller's deinit cannot be called until the timer's strong reference to the view controller is resolved. By using [weak self] capture list in the closure, we break that cycle, allowing the view controller to be deallocated when it's dismissed and it will stop the timer for you.
回答2:
This is not the best approach as the timer is not guaranteed to be accurate enough, it will drift over time. A better approach is to record a start time and use the calendar functions to give you the elapsed time. e.g.
let startTime = Date()
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
let now = Date()
let components = Calendar.current.dateComponents([.hour, .minute, .second], from: startTime, to: now)
guard let hoursPortion = components.hour, let minutesPortion = components.minute, let secondsPortion = components.second
else {
preconditionFailure("Should not happen")
}
self.TimerDisplay.text = "\(hoursPortion):\(minutesPortion):\(secondsPortion)"
}
来源:https://stackoverflow.com/questions/44555604/swift-scheduledtimer-stopwatch-hours-portion-not-working