2016-09-28 46 views
0

所以我有一个计时器,它保存了使用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 

} 

任何帮助将不胜感激。如果您需要澄清,请告诉我。

+0

虽然,这个计时器/ timerClass听起来像一个地球变种对我来说。 – antonio081014

回答

0

您正在处理几个问题:在视图控制器之间传递一个对象,在将来某个时间触发代码,并在后台持续一个计时器。

只要您的程序在后台运行的计时器,您可以简单地计算从现在到目标时间之间的秒数,并为将来的秒数设置非重复计时器。没有理由每秒发射一次重复的计时器,并进行数学计算,看看你的时间是否已经过去。你这样做的方式会使CPU运行速度更快并且耗尽电池电量,所以将来可以更好地设置单个计时器。

接下来,在后台处理定时器。简短的答案是,你不能。应用程序实际上不会在很长时间内在后台运行。他们很快就会被暂停,这是一个他们没有得到任何CPU时间的状态。你可以要求背景时间,但系统限制你3分钟。你可以玩技巧来获得超过3分钟的背景时间,但这些技巧会导致苹果拒绝你的应用程序,并且如果你设法潜入苹果,它会很快耗尽用户的电池。 (当一个应用程序在后台运行时,手机无法进入睡眠状态,CPU保持完全通电状态,吸入的电流比睡眠状态下的电流大得多。一个视图控制器到下一个视图控制器是的,你当然可以使用一个单例来使计时器成为一个共享资源,你也可以设置你的两个视图控制器,以便在第一个调用第二个视图的代码中,控制器将一个委托指针返回到第一个状态,并设置一个协议,让您将计时器从第二个视图控制器传回到第一个。

但是,计时器会调用单个目标,从任一视图控制器使用单例模式或del访问定时器自动模式,计时器仍会调用您在设置时使用的原始目标。

你可以让你的单身人士成为定时器的目标,给单身人士一个委托,并让单身人士在定时器发动时发送消息给委托人。然后当你切换视图控制器时,你可以改变单例的委托来指向新的视图控制器。

或者你可以在你的viewWillDisappear方法中记录你的计时器的“启动日期”,使计时器无效,并创建一个具有相同启动日期的新计时器(实际上你必须做一些数学转换火灾日期几秒钟,但它将是一个单一的电话。)

你也可以使用本地通知与未来的火灾日期,并设置它播放声音。但是,除非用户响应通知,否则不会从后台调用您的程序。

+0

非常感谢您详细的回答,但是如果您阅读我的代码,它会每秒触发一次,而不是每隔1/10。我的计时器保存结束日期,然后再次启动计时器并比较两次。进入后台时不会运行代码,它只会将当前的Date()时间与结束日期进行比较。很多好的信息,如果你能给我几个代码示例,这将是有益的。 –

+0

没有,此行:'定时器= Timer.scheduledTimer(一个时间间隔:0.1' ...创建一个触发间隔为1/10秒计时器在任何情况下,我的评论表示您可以只计算秒的量,直到你的目标。然后创建一个单一的非重复计时器,在您的目标时间触发。 –

相关问题