Stopwatch app counter going too fast - ios

I am just learning iOS and programming in general and I am making a very basic iOS stopwatch app. I got the stopwatch working however, when I press start more than once the timer begins to go faster so that it is no longer a second long (gif here). Also, my formatting seems to be off for the seconds part, if you have any suggestions there it would be appreciated. Here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var minuteLabel: UILabel!
#IBOutlet weak var secondLabel: UILabel!
var timer = NSTimer()
var second = 0
var minute = 0
func updateTime() {
do{
if second != 59
{
second++
secondLabel.text = ".\(second)"
}
else
{
second = 0
minute++
secondLabel.text = "." + String(format:"$%.2f", second)
if minute < 10
{
minuteLabel.text = "0\(minute)"
}
else
{
minuteLabel.text = String(format:"$%.2f", minute)
}
}
}
}
#IBAction func resetButton(sender: AnyObject) {
timer.invalidate()
second = 0
minute = 0
secondLabel.text = ".00"
minuteLabel.text = "00"
}
#IBAction func stopButton(sender: AnyObject) {
timer.invalidate()
}
#IBAction func startButton(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
Thank you for your help!

You are not invalidating timer when startButton is called, so tapping "Start" multiple times is creating duplicate timers which call the same function, updateTime. Change startButton to look like this:
#IBAction func startButton(sender: AnyObject) {
if !timer.valid
{
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
}
For your second question about the formatting, you need to put a condition to check for the seconds being less than 10, similar to what you did with the minutes. You would put a 0 in front of the seconds. In updateTime:
if second < 10
{
second++
secondLabel.text = ".0\(second)"
}
else if second <= 59
{
second++
secondLabel.text = ".\(second)"
}
else
{
...
}
See NSTimer documentation for more information.

You can get the desired formatting by using "%02d" instead of %.2f. Using %.2f will ensure 2 digits after the decimal point which is not true in your case (you have both second and minute as integers). Also you can use get away with using one label (e.g., timerLabel) as shown below:
var second = 0
var minute = 0
func updateTime() {
let dFormat = "%02d"
second++
if second == 59{
minute++
second = 0
}
let s = "\String(format: dFormat, minute):\String(format: dFormat, second))"
timerLabel.text = s
}
Hope this helps!

Related

Updating a timer in ios when app goes in and out of background

I have an app that has a timer in a view controller that counts down from 5 minutes to 0:00.
It stops when the app goes into the background per ios/Apple rules of app suspension.
How do I grab the time before it goes to sleep and update it in seconds when it comes back? I know you can't use background processing for long so avoiding that.
Seems like a good use case for UserDefaults.
This can be done in your main view controller.
ViewController.swift
let defaults = UserDefaults.standard
var timer = Timer()
var timerCount = 300
override func viewDidLoad() {
super.viewDidLoad()
let timeLeft = defaults.integer(forKey: "timerLeft1")
if timeLeft != 0 {
timerCount = timeLeft
}
startTimer()
}
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
}
#objc func updateCounter() {
if timerCount > 0 {
timerCount -= 1
defaults.set(timerCount, forKey: "timerLeft1")
} else {
timerCount = 300
}
}
Apple docs: UserDefaults

Making time in the app go down when closed/in background plus sending notifications

I want my app to keep the countdown going once the app goes into background, and when it reaches a certain number, lets say 10 hours left, it sends the user a notification saying that
This is the code I am using so far
I googled some stuff however it seems way too advanced to what I know so far with app saving and checking time later, are there any good tutorials, or does anybody know how can i solve that issue?
class ViewController: UIViewController {
public var streakNumber = 0
public var activated = false
//Labels
#IBOutlet var streakNumberLabel: UILabel!
#IBOutlet var remainingTimeTextLabel: UILabel!
#IBOutlet var remainingTimeNumberLabel: UILabel!
//Buttons
#IBAction func startButton(_ sender: Any) {
Activate()
}
#IBAction func stopButton(_ sender: Any) {
// Deactivate()
}
//Timer
var timer = Timer()
var seconds = 59
var minutes = 59
var hours = 47
//END
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func Activate (){
if activated == false {
streakNumber += 1
}
activated = true
streakNumberLabel.text = String(streakNumber)
}
func Deactivate (){
ActivTimer()
if activated == true{
activated = false
}
}
#objc func Clock(){
seconds = seconds-1
if seconds == -1{
seconds = 59
minutes = minutes-1
}
if minutes == -1{
minutes = 59
hours = hours-1
}
remainingTimeNumberLabel.text = (String(hours) + ":" + String(minutes) + ":" + String(seconds))
if seconds == 0 && hours == 0 && minutes == 0 {
timer.invalidate()
}
}
func ActivTimer(){
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.Clock), userInfo: nil, repeats: true)
}
}

Continuously check for response in Swift

I have a boolean variable called flag with initial value of false. Based on a successful process, it's set to true. There is a button alert, when tap it, it checks for flag's value along with a spinning image on UI, if flag is true, then a success message should displayed. otherwise, it should keep continuing response check (ten times for 5 seconds).
This is my functionality. I've been using NStimer to achieve this. Here is the code snippet:
var timer = NSTimer()
var count = 10
var flag: Bool = false
#IBOutlet weak var alert: UIButton!
#IBAction func alertAction(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: #selector(ViewController.prints), userInfo: nil, repeats: true)
}
func prints(){
if(count > 0)
{
if flag == false{
**Spinning Image**
count -= 1
} else {
count = 0
}
} else {
timer.invalidate()
}
}
The spinning image stops and continues after every 5 seconds ( in case response takes more than 5 seconds). I wish to spin the image continuously without a break. Can someone please help?
Thanks in advance!
Polling is the most desperate asynchronous pattern and almost always wrong. Learn a bit about value observation and reactive pattern.
var flag = false {
didSet {
if flag {
// stop spinning
}
}
}
func alertAction() {
// start spinning
}
From what I understand, this code will do what you intend. If you are using a UIActivityIndicator. Ensure to start it where I started the rotationAnimation and stop it when invalidating your timer.
Swift 3 Example
#IBOutlet weak var pin: UIImageView!
var timer: Timer?
var count: Int = 5
var flag: Bool {
return count == 0
}
#IBAction func buttonPressed(_ sender: AnyObject) {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0
rotationAnimation.toValue = 2 * M_PI
rotationAnimation.duration = 0.6
rotationAnimation.isCumulative = true
rotationAnimation.repeatCount = Float.infinity
pin.layer.add(rotationAnimation, forKey: "rotate")
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(prints), userInfo: nil, repeats: true)
}
func prints() {
if flag {
pin.layer.removeAllAnimations()
timer?.invalidate()
} else {
count = count - 1
}
}

IOS swift countdown timer will not stop

I am attempting to make a countdown timer that counts down from 60 seconds and then stops when it gets to 0. But for the timer keeps going into negative seconds. Any advice is appreciated. Code:
#IBOutlet var timeCounter: UILabel!
var second = 60
var timer = NSTimer()
var timerRunning = true
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setTimer()
timerCounting()
}
func setTimer(){
second -= 1
timeCounter.text = "\(second)"
}
func timerCounting(){
if(timerRunning == true){
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("setTimer"), userInfo: nil, repeats: true)
timerRunning = true
if second == 0 {
timerRunning = false
timer.invalidate()
}
}
}
You have to move the invalidation into the setTimer function since at its current location will never be triggered because timerCounting is only called once - in the beginning.
func setTimer(){
second -= 1
timeCounter.text = "\(second)"
if second == 0 {
timerRunning = false
timer.invalidate()
}
}
play with this in playground
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
import Foundation
#objc class C:NSObject {
var timer: NSTimer?
var second = 5
func setTimer(){
if c.second < 0 {
print("the timer fires the last time here ...")
timer?.invalidate()
} else {
print(second)
second -= 1
}
}
}
let c = C()
func timerCounting(){
c.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: c, selector: Selector("setTimer"), userInfo: nil, repeats: true)
}
timerCounting()

How to use a countdown timer to stop a game - iOS [SWIFT] -

My game is supposed to be stopped after 60s. But nothing happens with my timer code.
var timer = NSTimer()
var counter:Int = 60
var labelCounter:SKLabelNode = SKLabelNode()
Here is the code in my GameScene class :
func startTimer()
{
timer = NSTimer.scheduledTimerWithTimeInterval(1.0
, target: self, selector: Selector("updateTimer:"), userInfo: nil, repeats: true)
}
func updateTimer(dt:NSTimer)
{
counter--
if counter == 0 {
timer.invalidate()
removeCountDownTimerView()
} else{
labelCounter.text = "\(counter)"
}
}
func removeCountDownTimerView()
{
scene.view.paused = true
}
thank you for your insight :-)
Try something like this....
override func viewDidLoad() {
super.viewDidLoad()
//calling the wait function
self.callForWait()
}
func callForWait(){
//setting the delay time 60secs.
let delay = 60 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
//call the method which have the steps after delay.
self.stepsAfterDelay()
}
}
func stepsAfterDelay(){
//your code after delay takes place here...
}
I had this in a game. Hope it helps:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
startGame()
}
var startTime = NSTimeInterval()
var timer = NSTimer()
var gameTime:Double = 10
func startGame() {
let aSelector : Selector = "updateTime"
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: aSelector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
func updateTime() {
var currentTime = NSDate.timeIntervalSinceReferenceDate()
var elapsedTime = currentTime - startTime
var seconds = gameTime-elapsedTime
if seconds > 0 {
elapsedTime -= NSTimeInterval(seconds)
println("\(Int(seconds))")
} else {
timer.invalidate()
}
}
}

Resources