我希望我的倒数计时器暂停,然后在应用离开/返回焦点时恢复,使用时间来计算应扣除多少时间。
我正在使用app委托文件(不确定这是正确的位置?或者它们是否应该作为自己的函数位于视图控制器文件中?)
问题是我遇到了很多错误,例如:
类型的价值' AppDelegate'没有会员&restTimer'
使用未解析的标识符' nextFireDate'
使用未解析的标识符'选择器'
restTimer在我的视图控制器文件中被声明为计时器,但是当我在该文件中尝试这些块时,我得到了相同数量的未解析标识符错误
并使用以下2个代码块
func applicationWillResignActive(_ application: UIApplication) {
guard let t = self.restTimer else { return }
nextFireDate = t.fireDate
t.invalidate()
和
func applicationDidBecomeActive(_ application: UIApplication) {
guard let n = nextFireDate else { return }
let howMuchLonger = n.timeIntervalSinceDate(NSDate())
if howMuchLonger < 0 {
print("Should have already fired \(howMuchLonger) seconds ago")
target!.performSelector(selector!)
} else {
print("should fire in \(howMuchLonger) seconds")
Timer.scheduledTimerWithTimeInterval(howMuchLonger, target: target!, selector: selector!, userInfo: nil, repeats: false)
}
}
更新:由于包含答案的问题而添加了完整的观看代码
import Foundation
import UIKit
class RestController: UIViewController {
@IBOutlet weak var restRemainingCountdownLabel: UILabel!
@IBOutlet weak var setsRemainingCountdownLabel: UILabel!
@IBOutlet weak var numberOfSetsLabel: UILabel!
@IBOutlet weak var numberOfRestLabel: UILabel!
@IBOutlet weak var adjustSetsStepper: UIStepper!
@IBOutlet weak var adjustRestStepper: UIStepper!
var startDate: Date!
let startDateKey = "start.date"
let interval = TimeInterval(20)
var restTimer: Timer!
var restCount = 0
var setCount = 0
var selectedTime = 1
var selectedSets = 1
private let resignDateKey = "resign.date"
@IBAction func endSetPressed(_ sender: Any) {
if (setCount > 0){
setCount -= 1
setsRemainingCountdownLabel.text = String(setCount)
}
handleTimer()
}
@IBAction func setStepperValueChanged(_ sender: UIStepper) {
numberOfSetsLabel.text = Int(sender.value).description
self.setCount = Int(sender.value)
self.selectedSets = setCount
setsRemainingCountdownLabel.text = String(setCount)
}
@IBAction func restStepperValueChanged(_ sender: UIStepper) {
numberOfRestLabel.text = Int(sender.value).description
let timeMinSec = timeFormatted(totalSeconds: Int(sender.value)*60)
restRemainingCountdownLabel.text = timeMinSec
self.selectedTime = Int(sender.value)
restCount = self.selectedTime * 60
}
@IBAction func resetSetsButton(_ sender: Any) {
setCount = Int(adjustSetsStepper.value)
setsRemainingCountdownLabel.text = String(setCount)
}
override func viewDidLoad() {
super.viewDidLoad()
numberOfSetsLabel.text = String(selectedSets)
numberOfRestLabel.text = String(selectedTime)
createTimer(interval: interval)
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc private func willResignActive(notification: Notification) {
print("resigning")
guard restTimer.isValid else {
UserDefaults.standard.removeObject(forKey: startDateKey)
return
}
restTimer.invalidate()
UserDefaults.standard.set(Date(), forKey: startDateKey)
}
@objc private func didBecomeActive(notification: Notification) {
print("resume")
if let startDate = UserDefaults.standard.object(forKey: startDateKey) as? Date {
let elapsed = -startDate.timeIntervalSinceNow
print("elpased time: \(elapsed) remaining time: \(interval - elapsed)")
if elapsed > interval {
timerUp()
} else {
createTimer(interval: interval - elapsed)
}
}
}
private func createTimer (interval: TimeInterval) {
restTimer = Timer.scheduledTimer(withTimeInterval: interval , repeats: false) {[weak self] _ in
self?.timerUp()
}
startDate = Date()
}
private func timerUp() {
print("At least \(interval) seconds has elapsed")
}
func handleSets() {
if (setCount > 0) {
self.restCount = self.selectedTime * 60
}
handleTimer()
}
func handleTimer() {
if (restTimer?.isValid ?? false) {
restTimer?.invalidate()
restTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(RestController.updateTimer), userInfo: nil, repeats: true)
} else {
restTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(RestController.updateTimer), userInfo: nil, repeats: true)
}
}
func updateTimer() {
if (restCount > 0){
restCount -= 1
} else if (restCount == 0){
restTimer?.invalidate()
}
restRemainingCountdownLabel.text = timeFormatted(totalSeconds: restCount)
}
func timeFormatted(totalSeconds: Int) -> String {
let seconds: Int = totalSeconds % 60
let minutes: Int = (totalSeconds / 60) % 60
return String(format: "%02d:%02d", minutes, seconds)
}
答案 0 :(得分:0)
您不必为此使用AppDelegate,因为它也会发布通知。如果需要,您可以使用AppDelegate。以下是使用通知的代码:
class ViewController: UIViewController{
private let startDateKey = "start.date"
private let interval = TimeInterval(20)
private var startDate: Date!
private var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
createTimer(interval: interval)
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc private func willResignActive(notification: Notification) {
print("resigning")
guard timer.isValid else {
UserDefaults.standard.removeObject(forKey: startDateKey)
return
}
timer.invalidate()
UserDefaults.standard.set(Date(), forKey: startDateKey)
}
@objc private func didBecomeActive(notification: Notification) {
print("resume")
if let startDate = UserDefaults.standard.object(forKey: startDateKey) as? Date {
let elapsed = -startDate.timeIntervalSinceNow
print("elpased time: \(elapsed) remaining time: \(interval - elapsed)")
if elapsed > interval {
timerUp()
} else {
createTimer(interval: interval - elapsed)
}
}
}
private func createTimer (interval: TimeInterval) {
timer = Timer.scheduledTimer(withTimeInterval: interval , repeats: false) {[weak self] _ in
self?.timerUp()
}
startDate = Date()
}
private func timerUp() {
print("At least \(interval) seconds has elapsed")
}
}
答案 1 :(得分:0)
我认为您不能依赖应用程序在后台运行时保留在内存中的事实。因此,您应该在确切的位置存档重新创建计时器所需的所有数据。
例如,在applicationWillResignActive
UserDefaults.standard.set(value: t.nextFireDate forKey:"NextFireDate")
并在applicationWillEnterForeground
if let fireDate = UserDefaults.standard.object(forKey: "NextFireDate") {
// setup a timer with the correct fire date
}