I have a countdown Timer that shows seconds and milliseconds. The user can start/stop recording multiple times until the timer hits zero. The user can also delete a previous recording at which point I have to re-add that deleted time back into the initial 20 secs. There are 2 issues.
The first issue is when the timer is stopped, the remaining time that shows on the timer label doesn't match the time culmination of the recordings. From my understanding this might be a RunLoop issue and I don't think there is anything that I can do about the inaccuracies.
let initialTime = 20.0
var cumulativeTimeForAllAssests = 0.0
for asset in arrOfAssets {
let assetDuration = CMTimeGetSeconds(asset.duration)
print("assetDuration: ", assetDuration)
cumulativeTimeForAllAssests += assetDuration
}
print("\ncumulativeTimeForAllAssests: ", cumulativeTimeForAllAssests)
After starting/stopping 5 times, the remaining time on the timer label says 16.5 but the culmination of the assets time is 4.196666.... The timer label should say 15.8, it's 0.7 milli off. The more I start/stop the recording, the more inaccurate/further off the culmination time - the initial time and the timer label time is.
assetDuration: 0.7666666666666667
assetDuration: 0.9666666666666667
assetDuration: 0.7983333333333333
assetDuration: 0.7333333333333333
assetDuration: 0.9316666666666666
cumulativeTimeForAllAssests: 4.196666666666667
The second issue is because I'm using seconds and milliseconds in my timerLabel, when I add re-add the subtracted time back in via deleteAssetAndUpdateTimer(...), I use the parts of modf() to update the seconds and milliseconds. I couldn't think of another way to update the timer. I know there has to be a more accurate way to do it.
Timer code:
weak var timer: Timer?
var seconds = 20
var milliseconds = 0
let initialTime = 20.0
func startTimer() {
invalidateTimer()
if seconds == Int(initalTime) && milliseconds == 0 {
timerIsRunning()
}
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { [weak self] _ in
self?.timerIsRunning()
})
}
func timerIsRunning() {
updateTimerLabel()
if milliseconds == 0 {
seconds -= 1
}
milliseconds -= 1
if milliseconds < 0 {
milliseconds = 9
}
if seconds == 0 && milliseconds == 0 {
invalidateTimer()
updateTimerLabel()
}
}
func invalidateTimer() {
timer?.invalidate()
timer = nil
}
func updateTimerLabel() {
let milisecStr = "\(milliseconds)"
let secondsStr = seconds > 9 ? "\(seconds)" : "0\(seconds)"
timerLabel.text = "\(secondsStr).\(milisecStr)"
}
Delete asset and update timer code:
// the timer is stopped when this is called
func deleteAssetAndUpdateTimer(_ assetToDelete: AVURLAsset) {
var cumulativeTimeForAllAssests = 0.0
for asset in arrOfAssets {
let assetDuration = CMTimeGetSeconds(asset.duration)
cumulativeTimeForAllAssests += assetDuration
}
let timeFromAssetToDelete = CMTimeGetSeconds(assetToDelete.duration)
let remainingTime = self.initialTime - cumulativeTimeForAllAssests
let updatedTime = remainingTime + timeFromAssetToDelete
let mod = modf(updatedTime)
self.seconds = Int(mod.0)
self.milliseconds = Int(mod.1 * 10)
updateTimerLabel()
// remove assetToDelete from array
}
The big issue here was I was using a Timer to countdown which was incorrect. Following #LeoDabus' comments, I instead used CACurrentMediaTime():
let timerLabel = UILabel()
let maxRecordingTime = 30.0
lazy var elapsedTime = maxRecordingTime
var startTime: CFTimeInterval?
var endTime: CFTimeInterval?
weak var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
updateTimerLabel(with: Int(maxRecordingTime))
}
#IBAction func recordButtonPressed(_ sender: UIButton) {
if startTime == nil {
startTimer()
} else {
stopTimer(updateElapsed: true)
}
}
func startTimer() {
if elapsedTime == 0 { return }
stopTimer()
startTime = CACurrentMediaTime()
endTime = startTime! + elapsedTime
print("startTime: \(startTime!) | endTime: \(endTime!)")
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { [weak self] _ in
self?.timerIsRunning()
}
}
func timerIsRunning() {
guard let startTime = startTime, let endTime = endTime else { return }
let currentTime = CACurrentMediaTime()
let remainingTime = currentTime - startTime
print("%2d %.3lf", elapsedTime, remainingTime)
if currentTime >= endTime {
print("stopped at - currentTime: \(currentTime) | endTime: \(endTime)")
stopTimer(updateElapsed: true, currentTime: currentTime)
return
}
let countDownTime: Double = elapsedTime - remainingTime
let seconds = Int(countDownTime)
updateTimerLabel(with: seconds)
}
func updateTimerLabel(with seconds: Int) {
let secondsStr = seconds > 9 ? "\(seconds)" : "0\(seconds)"
timerLabel.text = secondsStr
}
func stopTimer(updateElapsed: Bool = false, currentTime: Double? = nil) {
timer?.invalidate()
timer = nil
if updateElapsed {
updateElapsedTime(using: currentTime)
}
startTime = nil
endTime = nil
}
func updateElapsedTime(using currentTime: Double? = nil) {
guard let startTime = startTime else { return }
var timeNow = CACurrentMediaTime()
if let currentTime = currentTime {
timeNow = currentTime
}
var updatedTime = elapsedTime - (timeNow - startTime)
if updatedTime < 0 {
updatedTime = 0
}
elapsedTime = updatedTime
}
func resetElapsedTime() { // This is for a resetButton not shown here
elapsedTime = maxRecordingTime
}
Related
I want to implement timer logic, when 5 min gets complete then my Tableview reload and its remove that particular index, I have tried not gets works, and timer get fast
//Timer ACtion Method
#objc func timerAction() {
if seconds>0 {
seconds-=1
minutes = String(format:"%02i",(seconds / 60))
seconds1 = String(format:"%02i",(seconds % 60))
print(minutes! + ":" + seconds1!)
self.lblMin.text = minutes!
self.lblSec.text = seconds1!
} else {
minutes = String(seconds / 60)
seconds1 = String(seconds % 60)
if minutes == "0" && seconds1 == "0" {
timer.invalidate()
btnReject.isUserInteractionEnabled = false
btnAccept.isUserInteractionEnabled = false
// TBVC.InstancePending.arrPending.remove(at: intValue!)
//tblData?.deleteRows(at: [IndexPath(row: intValue!, section: 1)], with: .automatic)
// TBVC.InstancePending.getTableBooking(strStatus: "0")
// TBVC.InstancePending.strTap = "Pending"
// TBVC.InstancePending.segment.selectedSegmentIndex = 0
// tblData?.reloadData()
}
}
}
Set Timer value nil, And check when API called then the timer will not pass any selector method
#objc func timerAction() {
if seconds>0 {
seconds-=1
minutes = String(format:"%02i",(seconds / 60))
seconds1 = String(format:"%02i",(seconds % 60))
print(minutes! + ":" + seconds1!)
self.lblMin.text = minutes!
self.lblSec.text = seconds1!
} else {
minutes = String(seconds / 60)
seconds1 = String(seconds % 60)
if minutes == "0" && seconds1 == "0" {
timer.invalidate()
timer = nil
btnReject.isUserInteractionEnabled = false
btnAccept.isUserInteractionEnabled = false
// TBVC.InstancePending.arrPending.remove(at: intValue!)
//tblData?.deleteRows(at: [IndexPath(row: intValue!, section: 1)], with: .automatic)
// TBVC.InstancePending.getTableBooking(strStatus: "0")
// TBVC.InstancePending.strTap = "Pending"
// TBVC.InstancePending.segment.selectedSegmentIndex = 0
// tblData?.reloadData()
}
}
}
=====================================
2nd method to implement timer:-
Initialize Variable
var timer:Timer?
var totalMinut:Int = 2
var totalSecond:Int = 120
var timeLeft = 120
Add timer function
func setupTimer() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)
}
#objc func onTimerFires() {
var minutes: Int
var seconds: Int
if totalSecond == 1 {
timer?.invalidate()
timer = nil
}
totalSecond = totalSecond - 1
minutes = (totalSecond) / 60
seconds = (totalSecond) % 60
timerLabel.text = String(format: "%02d:%02d", minutes, seconds)
}
Call "setUpTimer" method where you have required. In my case, I have called it in the "viewDidLoad" method of a view controller
override func viewDidLoad() {
super.viewDidLoad()
setupTimer()
}
I am quite new to Swift and already learned a lot using the questions here.
In one of my first projects I try to write a soccer playtime timer app. The first timer is counting up after the whistle button is pressed showing the minutes played and the second timer is counting down to zero showing the minutes left to play. This works so far.
Now both timers should stop automatically when the halftime is over, so that I can start a third overtime timer. So far the invalidate statement of the timer is not working - both timers keep running. There seems to be something wrong with my if-statements, but at the moment I have no clue what. So any help would be very appreciated.
var countUpClock: Timer?
var countDownClock: Timer?
private var formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
func runPlaytimeClocks() {
let startTime = Date()
let countTime = Date() + 2700 //45min of playtime
if startTime <= countTime {
countUpClock = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.timePlayed.text = self?.formatter.string(from: startTime, to: Date())
}
}
else {
countUpClock?.invalidate()
}
if startTime <= countTime {
countDownClock = Timer.scheduledTimer(withTimeInterval: -1.0, repeats: true) { [weak self] _ in
self?.timetoPlay.text = self?.formatter.string(from: Date(), to: countTime)
}
}
else {
countDownClock?.invalidate()
}
Thank you very much for the replies.
I found exactly what I was looking for here: http://ioscake.com/swift-nstimer-in-background.html
I adapted the solution to my for clocks (CountUpClock, CountDownClock, OvertimeClock, HalftimeClock).
Do you have any suggestions what would be the best solution to start the second halftime of the soccer game?
So far the CountUpClock starts again at 0:00 when I press the whistle button after the halftime break. But it should keep running from minute 45:00 to 90:00 - while the CountDownClock should counting down from 45:00 to 0:00 again.
What would be the best solution for such a behaviour?
import UIKit
import UserNotifications
private let stopTimeKey = "stopTimeKey"
class ViewController: UIViewController {
//Outlets
#IBOutlet weak var timePlayed: UILabel!
#IBOutlet weak var timeToPlay: UILabel!
#IBOutlet weak var overtime: UILabel!
#IBOutlet weak var halftime: UILabel!
#IBOutlet weak var halftimeButton: UIButton!
private var stopTime: Date?
override func viewDidLoad() {
super.viewDidLoad()
registerForLocalNotifications()
stopTime = UserDefaults.standard.object(forKey: stopTimeKey) as? Date
if let time = stopTime {
if time > Date() {
startTimers(time, includeNotification: false)
} else {
notifyTimerCompleted()
}
}
}
private func registerForLocalNotifications() {
if #available(iOS 10, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
guard granted && error == nil else {
// display error
print("\(String(describing: error))")
return
}
}
} else {
let types: UIUserNotificationType = [.badge, .sound, .alert]
let settings = UIUserNotificationSettings(types: types, categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
}
//Actions
#IBAction func whistleButtonTapped(_ sender: UIButton) {
overtimeClock?.invalidate()
overtimeClock = nil
halftimeClock?.invalidate()
halftimeClock = nil
overtime.text = "00:00"
halftime.text = "00:00"
halftimeButton.isHidden = true
//add 10 seconds per halftime to try out
let time = Date() + 10
if time > Date() {
startTimers(time)
} else {
timeToPlay.text = "error"
}
}
#IBAction func halftimeButton(_ sender: UIButton) {
halftimeButtoPressed()
}
// Code for different Timers
private var countDownClock: Timer?
private var countUpClock: Timer?
var overtimeClock: Timer?
var halftimeClock: Timer?
private func startTimers(_ stopTime: Date, includeNotification: Bool = true) {
// save `stopTime` in case app is terminated
UserDefaults.standard.set(stopTime, forKey: stopTimeKey)
self.stopTime = stopTime
// start Timer
countDownClock = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(handleCountDownTimer(_:)), userInfo: nil, repeats: true)
countUpClock = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(handleCountUpTimer(_:)), userInfo: nil, repeats: true)
guard includeNotification else { return }
// start local notification (so we're notified if timer expires while app is not running)
if #available(iOS 10, *) {
let content = UNMutableNotificationContent()
content.title = "Overtime is starting soon"
content.body = "In 5 seconds the overtime will start"
content.sound = UNNotificationSound.default()
//5 seconds warning before overtime starts
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: stopTime.timeIntervalSinceNow - 5, repeats: false)
let notification = UNNotificationRequest(identifier: "timer", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(notification)
} else {
let notification = UILocalNotification()
//5 seconds warning before overtime starts
notification.fireDate = stopTime - 5
notification.alertBody = "Overtime is starting soon"
UIApplication.shared.scheduleLocalNotification(notification)
}
}
private func stopTimer() {
countDownClock?.invalidate()
countDownClock = nil
countUpClock?.invalidate()
countUpClock = nil
}
private func halftimeButtoPressed() {
overtimeClock?.invalidate()
overtimeClock = nil
startHalftimeClock()
halftimeButton.isHidden = true
}
private let dateComponentsFormatter: DateComponentsFormatter = {
let _formatter = DateComponentsFormatter()
_formatter.allowedUnits = [.minute, .second]
_formatter.unitsStyle = .positional
_formatter.zeroFormattingBehavior = .pad
return _formatter
}()
#objc func handleCountDownTimer(_ timer: Timer) {
let now = Date()
if stopTime! > now {
timeToPlay.text = dateComponentsFormatter.string(from: now, to: stopTime!)
} else {
stopTimer()
notifyTimerCompleted()
startOvertimeClock()
halftimeButton.isHidden = false
}
}
#objc func handleCountUpTimer(_ timer: Timer) {
//add 10 seconds per halftime to try out
let now = Date() + 10
if now > stopTime! {
timePlayed.text = dateComponentsFormatter.string(from: stopTime!, to: now)
} else {
stopTimer()
notifyTimerCompleted()
}
}
//Overtime Clock
#objc func startOvertimeClock() {
let startOvertime = Date()
overtimeClock = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
self?.overtime.text = self?.dateComponentsFormatter.string(from: startOvertime, to: Date())
}
}
//Halftime Clock
#objc func startHalftimeClock() {
let startHalftime = Date()
halftimeClock = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
self?.halftime.text = self?.dateComponentsFormatter.string(from: startHalftime, to: Date())
}
}
private func notifyTimerCompleted() {
timeToPlay.text = "End"
timePlayed.text = "End"
}
}
I have a Swift script below relating to Sleep Analysis.
Error:
View Controller has no member 'updateTime'.
I tried adding this target using file inspector and adding it to target membership, but the target itself won't show up, which is beyond weird. Any feedback will be greatly appreciated.
PS: Another error message not saying that 'error' is an unresolved identifier for an if != nil statement keeps popping up. Any help here, would also be highly appreciated.
import UIKit
import HealthKit
let healthStore = HKHealthStore()
class ViewController: UIViewController {
#IBOutlet var displayTimeLabel: UILabel!
var startTime = TimeInterval()
var timer:Timer = Timer()
var endTime: NSDate!
var alarmTime: NSDate!
func saveSleepAnalysis() {
//1. startTime(alarmTime) and endTime are NSDate Objects//
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
//we create a new object that we want to add into our Health app(This is our INBED object)//
let object1 = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.inBed.rawValue, start: self.alarmTime as Date, end: self.endTime as Date)
// Time to save the object//
healthStore.save(object1, withCompletion: { (success, errpr) -> Void in
if error != nil
{
return
}
if success {
print("My new data was saved in HealthKit")
} else {
//something happened again//
}
})
//This our ASLEEP object//
let object2 = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.asleep.rawValue, start: self.alarmTime as Date, end: self.endTime as Date)
//now we save our objects to our mainLibrary known as HealthStore
healthStore.save(object2, withCompletion: { (success, error) -> Void in
if error != nil {
//Something went wrong//
return
if success {
print("My new data (2: Asleep data) was saved into HealthKit")
} else {
//something happened again//
}
}
}
)}
func retrieveSleepAnalysis() {
//first, define our object type that we watn again in BOOLEAN FORMAT//
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
//use sortDescriptor to get teh recent data first: so from MostRecentData to PastData//
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
//we create our query with a block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit:30, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
//something happends//
return
}
if let result = tmpResult {
//then i want the computer to do something with my data//
for item in result {
if let sample = item as? HKCategorySample {
let value = (sample.value == HKCategoryValueSleepAnalysis.inBed.rawValue) ? "InBed" : "Asleep"
print("Healthkit sleep: \(sample.startDate) \(sample.endDate) = value: \(value)")
}
}
}
}
//finally, we execute our query: Print out our output file //
healthStore.execute(query)
}
}
func viewDidLoad() {
super.viewDidLoad()
let typestoRead = Set([
HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
])
let typestoShare = Set([
HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
])
healthStore.requestAuthorization(toShare: typestoShare, read: typestoRead) { (success, error) -> Void in
if success == false {
NSLog(" Display not allowed")
}
}
}
func updateTime() {
let currentTime = NSDate.timeIntervalSinceReferenceDate
//Find the difference between current time and start time.
var elapsedTime: TimeInterval = currentTime - startTime
//calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (TimeInterval(minutes) * 60)
//calculate the seconds in elapsed time.
let seconds = UInt8(elapsedTime)
elapsedTime -= TimeInterval(seconds)
//find out the fraction of milliseconds to be displayed.
let fraction = UInt8(elapsedTime * 100)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let strFraction = String(format: "%02d", fraction)
//concatenate minuets, seconds and milliseconds as assign it to the UILabel
displayTimeLabel.text = "\(strMinutes):\(strSeconds):\(strFraction)"
}
func start(sender: AnyObject) {
alarmTime = NSDate()
if (!timer.isValid) {
let Selector : Selector = #selector(ViewController.updateTime)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: Selector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate
}
}
func stop(sender: AnyObject) {
endTime = NSDate()
saveSleepAnalysis()
retrieveSleepAnalysis()
timer.invalidate()
}
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
Replace your code with this
import UIKit
import HealthKit
let healthStore = HKHealthStore()
class ViewController: UIViewController {
#IBOutlet var displayTimeLabel: UILabel!
var startTime = TimeInterval()
var timer:Timer = Timer()
var endTime: NSDate!
var alarmTime: NSDate!
func saveSleepAnalysis() {
//1. startTime(alarmTime) and endTime are NSDate Objects//
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
//we create a new object that we want to add into our Health app(This is our INBED object)//
let object1 = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.inBed.rawValue, start: self.alarmTime as Date, end: self.endTime as Date)
// Time to save the object//
healthStore.save(object1, withCompletion: { (success, errpr) -> Void in
if errpr != nil
{
return
}
if success {
print("My new data was saved in HealthKit")
} else {
//something happened again//
}
})
//This our ASLEEP object//
let object2 = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.asleep.rawValue, start: self.alarmTime as Date, end: self.endTime as Date)
//now we save our objects to our mainLibrary known as HealthStore
healthStore.save(object2, withCompletion: { (success, error) -> Void in
if error != nil {
//Something went wrong//
return
if success {
print("My new data (2: Asleep data) was saved into HealthKit")
} else {
//something happened again//
}
}
}
)}
}
func retrieveSleepAnalysis() {
//first, define our object type that we watn again in BOOLEAN FORMAT//
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
//use sortDescriptor to get teh recent data first: so from MostRecentData to PastData//
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
//we create our query with a block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit:30, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
//something happends//
return
}
if let result = tmpResult {
//then i want the computer to do something with my data//
for item in result {
if let sample = item as? HKCategorySample {
let value = (sample.value == HKCategoryValueSleepAnalysis.inBed.rawValue) ? "InBed" : "Asleep"
print("Healthkit sleep: \(sample.startDate) \(sample.endDate) = value: \(value)")
}
}
}
}
//finally, we execute our query: Print out our output file //
healthStore.execute(query)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let typestoRead = Set([
HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
])
let typestoShare = Set([
HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
])
healthStore.requestAuthorization(toShare: typestoShare, read: typestoRead) { (success, error) -> Void in
if success == false {
NSLog(" Display not allowed")
}
}
}
func updateTime() {
let currentTime = NSDate.timeIntervalSinceReferenceDate
//Find the difference between current time and start time.
var elapsedTime: TimeInterval = currentTime - startTime
//calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (TimeInterval(minutes) * 60)
//calculate the seconds in elapsed time.
let seconds = UInt8(elapsedTime)
elapsedTime -= TimeInterval(seconds)
//find out the fraction of milliseconds to be displayed.
let fraction = UInt8(elapsedTime * 100)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let strFraction = String(format: "%02d", fraction)
//concatenate minuets, seconds and milliseconds as assign it to the UILabel
displayTimeLabel.text = "\(strMinutes):\(strSeconds):\(strFraction)"
}
func start(sender: AnyObject) {
alarmTime = NSDate()
if (!timer.isValid) {
let Selector : Selector = #selector(ViewController.updateTime)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: Selector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate
}
}
func stop(sender: AnyObject) {
endTime = NSDate()
saveSleepAnalysis()
retrieveSleepAnalysis()
timer.invalidate()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The high score in my game is time based and I am having trouble setting comparing it with the current score. This is my code so far which doesn't work:
class GameScene: SKScene, SKPhysicsContactDelegate {
// initialise value for current time
var currentTime = NSDate()
var bestTime = NSDate()
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
// set the current time to 0 seconds
var date0 = NSDate();
let timeInterval = floor(date0 .timeIntervalSinceReferenceDate / 60.0) * 60.0
date0 = NSDate(timeIntervalSinceReferenceDate: timeInterval)
currentTime = date0
// call to start timer
NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "printDuration:", userInfo: NSDate(), repeats: true)
Then
func printDuration(timer: NSTimer) {
if self.view?.paused == false {
guard let userInfo = timer.userInfo else {
return
}
guard let startDate = userInfo as? NSDate else {
return
}
let duration = NSDate().timeIntervalSinceDate(startDate)
currentTime = NSDate(timeIntervalSinceReferenceDate: duration)
currentTimeValueLabel.text = "\(NSString(format:"%3.2f", duration))"
}
}
I want to be able to do something like below where I am able to compare the time in both variables and set the higher one accordingly:
if (currentTime > highScore) {
highScore = currentTime
highScoreLabel.text = "\(NSString(format:"%3.2f", highScore))"
}
You can set startDate = NSDate() in didMoveToView() at the beginning and then compare the two to see if duration is higher by typing
if duration > highScore {
highScore = duration
}
I just made a stopwatch with a tutorial but what I would like to do is to update my 00:00 label as 1 second increasing such as 00:01, 00:02: 00:03 and to do the same for minutes. Is there anyway of doing that? Thanks in advance!
Then you have to get the date which will start the counting from which is the current date when a particular event occurs, let's say we will start the timer when the view appears, so implement viewWillAppear as follows:
var currentDate = NSDate()
override func viewWillAppear(animated: Bool) {
currentDate = NSDate()
var timer: NSTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateLabel", userInfo: nil, repeats: true)
timer.fire()
}
and implement the updateLabel function:
func updateLabel() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
var elapsedSeconds: NSTimeInterval = -self.currentDate.timeIntervalSinceNow
let minutes: Int = Int(elapsedSeconds)/60
let seconds: Int = Int(elapsedSeconds) - (minutes*60)
self.timeLabel.text = String(format: "%02d:%02d", minutes, seconds)
})
}
When formatting time elapsed, NSDateComponentsFormatter is another option:
var start: CFAbsoluteTime!
override func viewDidLoad() {
super.viewDidLoad()
start = CFAbsoluteTimeGetCurrent()
NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "handleTimer:", userInfo: nil, repeats: true)
}
lazy var formatter: NSDateComponentsFormatter = {
let _formatter = NSDateComponentsFormatter()
_formatter.allowedUnits = .CalendarUnitMinute | .CalendarUnitSecond
_formatter.zeroFormattingBehavior = .Pad
return _formatter
}()
func handleTimer(timer: NSTimer) {
let elapsed = CFAbsoluteTimeGetCurrent() - start
label.text = formatter.stringFromTimeInterval(elapsed)
}
Admittedly, that will give you the time elapsed in 0:00 format, not 00:00 format.
This is Objective-C, but you'll get the idea:
-(void) updateTotalTime
{
int forHours = timeInSeconds / 3600,
remainder = timeInSeconds % 3600,
forMinutes = remainder / 60,
forSeconds = remainder % 60;
[elapsedTime setString:[NSString stringWithFormat:NSLocalizedString(#"elapsedTime", nil)
,forHours
,forMinutes
,forSeconds]];
}
and in my Localizable.strings:
"elapsedTime" = "Time: %02d:%02d:%02d";