如何将计时器从第二个视图控制器传递给第一个?
所以我有一个计时器,它保存了使用NSUserDefaults保存结束时间,但我也想把这个计时器推到前面的ViewController。计时器应在第二个视图控制器上启动,并且如果您返回,或退出应用程序并重新打开该计时器,则应显示计时器。我对如何使用Singleton DataService有一个想法,但不太清楚如何把它放在一起。截至目前,这是我的代码。如何将计时器从第二个视图控制器传递给第一个?
import UIKit
import UserNotifications
let stopTimeKey = "stopTimeKey"
class QOTDVC: UIViewController {
// TIMER VARIABLES
let timeInterval: Double = 89893
let defaults = UserDefaults.standard
var expirationDate = NSDate()
@IBOutlet weak var timerLabel: UILabel!
@IBAction func DoneWithQuestion(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
@IBOutlet weak var timerCounter: UILabel!
var timer: Timer?
var stopTime: Date?
override func viewDidLoad() {
super.viewDidLoad()
saveStopTime()
NotificationCenter.default.addObserver(self, selector: #selector(saveStopTime), name: NSNotification.Name(rawValue: "Date") , object: nil)
}
func alert(message: String, title: String = "") {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default) {
UIAlertAction in
self.registerForLocalNotifications()
StartTimerInitiated()
}
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
func registerForLocalNotifications() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
if granted {
let content = UNMutableNotificationContent()
content.title = "Ready for the QOTD"
content.body = "You have 30 seconds to answer the question"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: self.timeInterval , repeats: false)
let request = UNNotificationRequest(identifier: "myTrigger", content: content, trigger: trigger)
center.add(request)
}
}
}
func StartTimerInitiated() {
let time = Date(timeIntervalSinceNow: timeInterval)
if time.compare(Date()) == .orderedDescending {
startTimer(stopTime: time)
} else {
timerLabel.text = "timer date must be in future"
}
}
// MARK: Timer stuff
func startTimer(stopTime: Date) {
// save `stopTime` in case app is terminated
UserDefaults.standard.set(stopTime, forKey: stopTimeKey)
self.stopTime = stopTime
// start NSTimer
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(QOTDVC.handleTimer), userInfo: nil, repeats: true)
// start local notification (so we're notified if timer expires while app is not running)
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
let dateComponentsFormatter: DateComponentsFormatter = {
let _formatter = DateComponentsFormatter()
_formatter.allowedUnits = [.hour, .minute, .second]
_formatter.unitsStyle = .positional
_formatter.zeroFormattingBehavior = .pad
return _formatter
}()
func handleTimer(timer: Timer) {
let now = Date()
if stopTime!.compare(now) == .orderedDescending {
timerLabel.text = dateComponentsFormatter.string(from: now, to: stopTime!)
} else {
stopTimer()
notifyTimerCompleted()
}
}
func notifyTimerCompleted() {
timerLabel.text = "Timer done!"
}
func saveStopTime() {
stopTime = UserDefaults.standard.object(forKey: stopTimeKey) as? Date
if let time = stopTime {
if time.compare(Date()) == .orderedDescending {
startTimer(stopTime: time)
} else {
notifyTimerCompleted()
}
}
stopTime = UserDefaults.standard.object(forKey: stopTimeKey) as? Date
}
任何帮助将不胜感激。如果您需要澄清,请告诉我。
您正在处理几个问题:在视图控制器之间传递一个对象,在将来某个时间触发代码,并在后台持续一个计时器。
只要您的程序在后台运行的计时器,您可以简单地计算从现在到目标时间之间的秒数,并为将来的秒数设置非重复计时器。没有理由每秒发射一次重复的计时器,并进行数学计算,看看你的时间是否已经过去。你这样做的方式会使CPU运行速度更快并且耗尽电池电量,所以将来可以更好地设置单个计时器。
接下来,在后台处理定时器。简短的答案是,你不能。应用程序实际上不会在很长时间内在后台运行。他们很快就会被暂停,这是一个他们没有得到任何CPU时间的状态。你可以要求背景时间,但系统限制你3分钟。你可以玩技巧来获得超过3分钟的背景时间,但这些技巧会导致苹果拒绝你的应用程序,并且如果你设法潜入苹果,它会很快耗尽用户的电池。 (当一个应用程序在后台运行时,手机无法进入睡眠状态,CPU保持完全通电状态,吸入的电流比睡眠状态下的电流大得多。一个视图控制器到下一个视图控制器是的,你当然可以使用一个单例来使计时器成为一个共享资源,你也可以设置你的两个视图控制器,以便在第一个调用第二个视图的代码中,控制器将一个委托指针返回到第一个状态,并设置一个协议,让您将计时器从第二个视图控制器传回到第一个。
但是,计时器会调用单个目标,从任一视图控制器使用单例模式或del访问定时器自动模式,计时器仍会调用您在设置时使用的原始目标。
你可以让你的单身人士成为定时器的目标,给单身人士一个委托,并让单身人士在定时器发动时发送消息给委托人。然后当你切换视图控制器时,你可以改变单例的委托来指向新的视图控制器。
或者你可以在你的viewWillDisappear方法中记录你的计时器的“启动日期”,使计时器无效,并创建一个具有相同启动日期的新计时器(实际上你必须做一些数学转换火灾日期几秒钟,但它将是一个单一的电话。)
你也可以使用本地通知与未来的火灾日期,并设置它播放声音。但是,除非用户响应通知,否则不会从后台调用您的程序。
非常感谢您详细的回答,但是如果您阅读我的代码,它会每秒触发一次,而不是每隔1/10。我的计时器保存结束日期,然后再次启动计时器并比较两次。进入后台时不会运行代码,它只会将当前的Date()时间与结束日期进行比较。很多好的信息,如果你能给我几个代码示例,这将是有益的。 –
没有,此行:'定时器= Timer.scheduledTimer(一个时间间隔:0.1' ...创建一个触发间隔为1/10秒计时器在任何情况下,我的评论表示您可以只计算秒的量,直到你的目标。然后创建一个单一的非重复计时器,在您的目标时间触发。 –
虽然,这个计时器/ timerClass听起来像一个地球变种对我来说。 – antonio081014