Вместо того, чтобы пытаться запустить таймер в фоновом режиме, запишите startDate
начала тренировки и вычислите временной интервал. Таким образом, приложение не должно запускаться в фоновом режиме, чтобы отслеживать время тренировки. Таймер будет использоваться только для обновления пользовательского интерфейса.
Пауза теперь работает, записывая текущий интервал тренировки. Когда тренировка возобновляется, она вычитает текущий интервал тренировки из Date()
, чтобы получить новый скорректированный startDate
.
Добавьте уведомления для приложения, вводящего фон и передний план, чтобы можно было перезапустить таймер обновления интерфейса пользователя, если тренировка активна:
import UIKit
enum WorkoutState {
case inactive
case active
case paused
}
class ViewController: UIViewController {
var workoutState = WorkoutState.inactive
var workoutInterval = 0.0
var startDate = Date()
var timer = Timer()
@IBOutlet weak var outputLabel: UILabel!
@IBOutlet weak var start: UIButton!
@IBOutlet weak var paused: UIButton!
@IBAction func startButton(_ sender: UIButton) {
startButtonPressed()
}
@IBAction func pausedButton(_ sender: UIButton) {
pausedButtonPressed()
}
@IBOutlet weak var timerLabel: UILabel!
func updateTimerLabel() {
let interval = -Int(startDate.timeIntervalSinceNow)
let hours = interval / 3600
let minutes = interval / 60 % 60
let seconds = interval % 60
timerLabel.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
func startButtonPressed() {
if workoutState == .inactive {
startDate = Date()
} else if workoutState == .paused {
startDate = Date().addingTimeInterval(-workoutInterval)
}
workoutState = .active
outputLabel.text = "Workout Started"
start.isHidden = true
paused.isHidden = false
updateTimerLabel()
_foregroundTimer(repeated: true)
print("Calling _foregroundTimer(_:)")
}
func pausedButtonPressed(){
// record workout duration
workoutInterval = floor(-startDate.timeIntervalSinceNow)
outputLabel.text = "Workout Paused"
workoutState = .paused
timer.invalidate()
pauseWorkout()
}
func pauseWorkout(){
paused.isHidden = true
start.isHidden = false
}
func _foregroundTimer(repeated: Bool) -> Void {
NSLog("_foregroundTimer invoked.");
//Define a Timer
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerAction(_:)), userInfo: nil, repeats: true);
print("Starting timer")
}
@objc func timerAction(_ timer: Timer) {
print("timerAction(_:)")
self.updateTimerLabel()
}
@objc func observerMethod(notification: NSNotification) {
if notification.name == .UIApplicationDidEnterBackground {
print("app entering background")
// stop UI update
timer.invalidate()
} else if notification.name == .UIApplicationDidBecomeActive {
print("app entering foreground")
if workoutState == .active {
updateTimerLabel()
_foregroundTimer(repeated: true)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(observerMethod), name: .UIApplicationDidEnterBackground, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(observerMethod), name: .UIApplicationDidBecomeActive, object: nil)
print("viewDidLoad()")
print("Hiding buttons")
paused.isHidden = true
start.isHidden = false
print("Clearing Labels")
outputLabel.text = ""
timerLabel.text = ""
print("\(timer)")
timer.invalidate()
}
}
Оригинальный ответ
Просто позвоните updateTimerLabel()
в основном цикле:
DispatchQueue.main.async {
self.updateTimerLabel()
}
Полная функция:
@objc func _backgroundTimerAction(_ timer: Timer) {
print("_backgroundTimerAction(_:)")
time += 1
DispatchQueue.main.async {
self.updateTimerLabel()
}
NSLog("time count -> \(time)")
}
Примечания:
- Запуск таймера в фоновом потоке не приносит вам ничего, кроме проблем с его настройкой. Я бы порекомендовал просто запустить его в главном потоке.
- Нет необходимости добавлять
-> Void
в определение функции Swift; это значение по умолчанию.
- Свифт обычно не нуждается в точках с запятой
;
, поэтому теряйте их.
self.time
уже является Int
, поэтому создавать из него Int
не нужно.
заменить:
let hours = Int(self.time) / 3600
с:
let hours = self.time / 3600