Updating time function every second in NSDate() - Swift - ios

I have a function that gives me a time when the app is opened but I don't want it to be static I want it to be dynamic, updating every second. I thought of it being in a loop, but I don't know how to go about doing it. Doing a loop could work but if you have a better way of doing it please answer. Here is my function:
func timeNowString() -> NSString {
let date = NSDate()
var outputFormat = NSDateFormatter()
outputFormat.locale = NSLocale(localeIdentifier:"en_US")
outputFormat.dateFormat = "HH:mm:ss"
let timeString = outputFormat.stringFromDate(date)
return timeString;
}
Question: How do I make this function dynamic? So it goes and updates every second instead of it being a static label.
If you have any questions please comment down below.

Try this. You'll need to add a label and a button and hook them up to the textLabel and start IBAction. You can modify this to add hours (just minutes/seconds here) and add a timer.invalidate() where you need it.
import UIKit
class ViewController: UIViewController {
#IBOutlet var timeLabel: UILabel!
#IBAction func start(sender: AnyObject) {
var timer = NSTimer()
if !timer.valid {
let selector : Selector = "countTime"
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target:self, selector: selector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
let timeNow = timeNowString() as String
for item in timeNow {
time = timeNow.componentsSeparatedByString(":")
}
}
var time = [String]()
var startTime = NSTimeInterval()
override func viewDidLoad() {
super.viewDidLoad()
}
func countTime() {
var currentTime = NSDate.timeIntervalSinceReferenceDate()
var elapsedTime: NSTimeInterval = currentTime - startTime
var adjustedTime = Int(elapsedTime) + 3600*time[0].toInt()! + 60*time[1].toInt()! + time[0].toInt()!
var hours = Int(Double(adjustedTime)/3600.0)
let minutes = Int(Double(adjustedTime - hours*3600)/60.0)
let seconds = adjustedTime - hours*3600 - minutes*60
let startHours = hours > 9 ? String(hours):"0" + String(hours)
let startMinutes = minutes > 9 ? String(minutes):"0" + String(minutes)
let startSeconds = seconds > 9 ? String(seconds):"0" + String(seconds)
timeLabel.text = "\(startHours):\(startMinutes):\(startSeconds)"
}
func timeNowString() -> NSString {
let date = NSDate()
var outputFormat = NSDateFormatter()
outputFormat.locale = NSLocale(localeIdentifier:"en_US")
outputFormat.dateFormat = "HH:mm:ss"
let timeString = outputFormat.stringFromDate(date)
return timeString;
}
}

Related

Swift - Re-add time back into Timer

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
}

Set high score as elapsed time in iOS

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
}

Adding and Subtracting times in Swift

I've written some of this in pseudo code because I don't know the syntax for it. I'd like to have the timeLeftLabel.text reflect how many hours, minutes, and seconds are left until the 6 hours are up. My biggest problem is that I don't know how to add and subtract times. Can anyone help me?
var timer = NSTimer()
func timerResults() {
let theDate = NSDate()
var endTime = theDate //+ 6 hours
let timeLeft = endTime //- theDate
timeLeftLabel.text = "\(timeLeft)"
}
#IBOutlet weak var timeLeftLabel: UILabel!
#IBAction func IBbtnUpdateTap(sender: UIButton){
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("timerResults"), userInfo: nil, repeats: true)
}
Assuming your deployment target is iOS 8.0 or later, you should use NSDateComponentsFormatter to format your string. You want something like this:
class MyViewController : UIViewController {
#IBOutlet weak var timeLeftLabel: UILabel!
var targetDate: NSDate?
var labelUpdateTimer: NSTimer?
var timeLeftFormatter: NSDateComponentsFormatter?
#IBAction func startTimerButtonWasTapped() {
targetDate = NSDate(timeIntervalSinceNow: 6 * 60 * 60)
labelUpdateTimer = NSTimer.scheduledTimerWithTimeInterval(1,
target: self, selector: "labelUpdateTimerDidFire:", userInfo: nil, repeats: true)
timeLeftFormatter = NSDateComponentsFormatter()
timeLeftFormatter?.unitsStyle = .Abbreviated // gives e.g. "1h 20m 34s"
// You might prefer .Positional, which gives e.g. "1:20:34"
timeLeftFormatter?.allowedUnits = [ .Hour, .Minute, .Second ]
labelUpdateTimerDidFire(labelUpdateTimer!)
}
#objc func labelUpdateTimerDidFire(timer: NSTimer) {
let now = NSDate()
timeLeftLabel.text = timeLeftFormatter!.stringFromDate(now,
toDate: targetDate!)
if now.compare(targetDate!) != NSComparisonResult.OrderedAscending {
print("times up!")
labelUpdateTimer?.invalidate()
}
}
}
This will add 6 hours:
let future = now.dateByAddingTimeInterval(3600*6) // 1 hour is 3600 seconds
This will find the difference:
let difference = future.timeIntervalSinceDate(now)

NSTimer to to 4 digit label update

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";

Stuck: Analog Clock in Swift iOS

I'm new to swift (and coding in general), and I've been working on an analog watch project. I've gotten to a place where I'm stuck. None of the println() commands are putting anything out to the console unless they arrive before the function "func setTime(){...} ". Not sure what's happening there. And I can't seem to get the hands to rotate with the math I found here. I'm trying to convert it from obj-c to swift. (I know the bottom section is very wrong at the moment) I keep messing with it (the bottom section) but I don't get any rotation.
Any help would be appreciated. I feel like I'm close.
var theTimer:NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println(viewDidLoad)
func setTime(){
println("set time")
self.theTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "set time", userInfo: nil, repeats: false)
let date = NSDate()
let outputFormatter = NSDateFormatter()
outputFormatter.dateFormat = "hh:mm:ss"
let newDateString:NSString = outputFormatter.stringFromDate(date)
println(newDateString)
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond, fromDate: date)
var hour = components.hour
var minute = components.minute
var second = components.second
println(hour)
println(minute)
println(second)
**var secAngle = (6 * second)
var minAngle = (6 * minute)
var hourAngle = (30 * hour + minute / 2)
self.secsImage.transform = CGAffineTransformMakeRotation( CGFloat(secAngle) )
self.minsImage.transform = CGAffineTransformMakeRotation( CGFloat(Double(minAngle)) )
self.hoursImage.transform = CGAffineTransformMakeRotation( CGFloat(Double(hourAngle)) )**
println(secAngle)
println(minAngle)
println(hourAngle)
}
}
You have declared your setTime function inside your viewDidLoad function, and you have never called it. Also, your the selector for your NSTimer is not correct - it needs to be the name of the function and it needs to be a Selector, not just a string.
You should move the setTime function declaration out of viewDidLoad and I would suggest setting it up in viewWillAppear -
var theTimer:NSTimer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println(viewDidLoad)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.setTime()
self.theTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("setTime"), userInfo: nil, repeats: true)
}
func setTime(){
println("set time")
let date = NSDate()
let outputFormatter = NSDateFormatter()
outputFormatter.dateFormat = "hh:mm:ss"
let newDateString:NSString = outputFormatter.stringFromDate(date)
println(newDateString)
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond, fromDate: date)
var hour = components.hour
var minute = components.minute
var second = components.second
println(hour)
println(minute)
println(second)
var secAngle = (6 * second)
var minAngle = (6 * minute)
var hourAngle = (30 * hour + minute / 2)
self.secsImage.transform = CGAffineTransformMakeRotation( CGFloat(secAngle) )
self.minsImage.transform = CGAffineTransformMakeRotation( CGFloat(Double(minAngle)) )
self.hoursImage.transform = CGAffineTransformMakeRotation( CGFloat(Double(hourAngle)) )**
println(secAngle)
println(minAngle)
println(hourAngle)
}
If you are new to programming, you may want to consider running through some basic coding tutorials on the web.

Resources