J'ai une minuterie 90s qui s'exécute en arrière-plan jusqu'à la fin de l'enregistrement. beginBackgroundTaskWithExpirationHandler
. Cela semble simple, mais le problème que je rencontre est que la minuterie elle-même dérive considérablement. Pour le minuteur des années 90, j'obtiens environ 30 à 35 secondes de dérive. En d'autres termes, si je lance le minuteur, que je mets l'application en arrière-plan, puis que j'ouvre l'application après 90 secondes, le minuteur affiche 30 secondes restantes.
Si je laisse l'application ouverte pendant toute la durée des années 90, je n'obtiens aucune dérive. Si je réduis le timeInterval du timer à 1s (au lieu de mon préféré 0,05s), la dérive du fond disparaît.
Que puis-je faire pour éliminer la dérive de l'arrière-plan sans perdre la précision de la minuterie ?
class TimerViewController: UIViewController {
@IBOutlet weak var startTimerButton: UIButton!
@IBOutlet weak var timerLabel: UILabel!
@IBOutlet weak var resetTimerButton: UIButton!
var timer = NSTimer()
let timeInterval:NSTimeInterval = 0.05
let timerEnd:NSTimeInterval = 90
var timeCount:NSTimeInterval = 0
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
override func viewDidLoad() {
super.viewDidLoad()
resetTimeCount()
timerLabel.text = timeString(timeCount)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(TimerViewController.reinstateBackgroundTask), name: UIApplicationDidBecomeActiveNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Actions
@IBAction func startTimerButtonTapped(sender: UIButton) {
if !timer.valid { //prevent more than one timer on the thread
timerLabel.text = timeString(timeCount)
timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self,selector: #selector(TimerViewController.timerDidEnd(_:)),userInfo: nil, repeats: true)
schedulePushNotification()
registerBackgroundTask()
}
}
@IBAction func resetTimerButtonTapped(sender: UIButton) {
timer.invalidate()
resetTimeCount()
timerLabel.text = timeString(timeCount)
cancelAllNotifications()
}
// MARK: Timer
func resetTimeCount(){
timeCount = timerEnd
}
func timerDidEnd(timer: NSTimer){
//timer that counts down
timeCount = timeCount - timeInterval
if timeCount <= 0 { //test for target time reached.
timerLabel.text = "Time is up!!"
timer.invalidate()
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
if backgroundTask != UIBackgroundTaskInvalid {
endBackgroundTask()
cancelAllNotifications()
}
} else { //update the time on the clock if not reached
timerLabel.text = timeString(timeCount)
}
}
func timeString(time: NSTimeInterval) -> String {
let minutes = Int(time) / 60
let seconds = time - Double(minutes) * 60
let secondsFraction = seconds - Double(Int(seconds))
return String(format:"%02i:%02i.%02i",minutes,Int(seconds),Int(secondsFraction * 100.0))
}
// MARK: BackgroundTask
func registerBackgroundTask() {
backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
[unowned self] in
self.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func reinstateBackgroundTask() {
if timeCount != 0.0 && (backgroundTask == UIBackgroundTaskInvalid) {
registerBackgroundTask()
}
}
func endBackgroundTask() {
UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
// MARK: Notifications
func schedulePushNotification() {
let notification = UILocalNotification()
notification.alertAction = "Go back to App"
notification.alertBody = "The 90s timer is finished!"
notification.fireDate = NSDate(timeIntervalSinceNow: timerEnd+1)
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
func cancelAllNotifications() {
UIApplication.sharedApplication().cancelAllLocalNotifications()
}
}