Timer Not Stopping When Supposed To Swift 2 - swift

I have coded my timer with the idea in mind that when my timer reaches 10, it stops. But for some reason, it doesn't.
import Foundation
import UIKit
class SinglePlayer: UIViewController {
var timerCount = 0.0
#IBOutlet weak var timer: UILabel!
var timerVar = NSTimer()
func isCounting() {
timerCount += 0.1
timer.text = "\(timerCount)"
}
override func viewDidLoad() {
super.viewDidLoad()
if timerCount <= 10.0{
timerVar = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "isCounting", userInfo: nil, repeats:true)
} else {
timerVar.invalidate()
}
}
}

You need to put condition in the callback function and not on viewDidLoad which is only called once on the load.
func isCounting() {
timerCount += 0.1
timer.text = "\(timerCount)"
if timerCount >= 10.0 {
timerVar.invalidate()
}
}

Related

Error in Swift 5 with Xcode when creating a timer out of a label

I'm using Swift 5 in Xcode to code an app. On one view controller, I am creating a timer, which counts from 20 minutes down to 0. I have what I think is a successful code, but it throws back one error. In the line
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)
it gives an error saying Type 'SkippingViewController' has no member 'updateTimer' (SkippingViewController is the name of the view controller for the page of the app my timer is on)
How can I resolve this issue?
import UIKit
class SkippingViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var startWorkoutButton: UIButton!
#IBOutlet weak var pauseWorkoutButton: UIButton!
var timer = Timer()
var counter = 20.00
var isRunning = false
override func viewDidLoad() {
super.viewDidLoad()
timeLabel.text = "\(counter)"
startWorkoutButton.isEnabled = true
pauseWorkoutButton.isEnabled = false
// Do any additional setup after loading the view.
}
#IBAction func startWorkoutButtonDidTouch(_ sender: Any) {
if !isRunning {
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)
startWorkoutButton.isEnabled = false
pauseWorkoutButton.isEnabled = true
isRunning = true
}
}
#IBAction func pauseWorkoutButtonDidTouch(_ sender: Any) {
func updateTimer() {
counter -= 0.01
timeLabel.text = String(format: "%.01f", counter)
}
}
}
Your problem is, that there is no method called 'updateTimer' in SkippingViewController.swift. You falsely put the method inside of the method 'pauseWorkoutButtonDidTouch'. In order to resolve the error insert the following code into SkippingViewController.swift:
#objc func updateTimer() {
counter -= 0.01
timeLabel.text = String(format: "%.01f", counter)
}

Swift: another timer starting when application enters background

I've been struggling to figure this out for a few days: I need to create a timer that the user can't kill, so once they start it, even if they kill the app or it enters background, it will pick up where it left off, and to achieve this I am saving the date when the app terminated and then calculating the difference, so that part works fine.
The problem I keep running into however is that a second timer seems to start if I minimise the app and bring it back. I'm not sure how timers are managed in the background, but nothing of what I tried (calling timer.invalidate() when applicationWillResignActive gets called, calling it in deinit() ) seems to work. The behaviour I see after this is that the timer will count like this: 80 - 78 - 79 - 76 - 77..
There's also a problem where the timer will sometime run past the time it's supposed to run for after killing the app, but I can't find the exact cause for that because it doesn't always happen.
Any idea what I'm doing wrong?
Thanks a lot.
class Focus: UIViewController {
// MARK: Variables
var timer = Timer()
let timeToFocus = UserDefaults.standard.double(forKey: "UDTimeToFocus")
let currentFocusedStats = UserDefaults.standard.integer(forKey: "UDFocusStats")
// MARK: Outlets
#IBOutlet weak var progress: KDCircularProgress!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var focusTimeLabel: UILabel!
#IBOutlet weak var stepNameLabel: UILabel!
#IBOutlet weak var focusAgain: UIButton!
#IBOutlet weak var allDone: UIButton!
#IBOutlet weak var help: UIButton!
#IBOutlet weak var dottedCircle: UIImageView!
// MARK: Outlet Functions
#IBAction func helpTU(_ sender: Any) { performSegue(withIdentifier: "ToFocusingHelp", sender: nil) }
#IBAction func helpTD(_ sender: Any) { help.tap(shape: .rectangle) }
#IBAction func allDoneTU(_ sender: Any) {
UserDefaults.standard.set(false, forKey: "UDFocusIsRunning")
UserDefaults.standard.set(false, forKey: "UDShouldStartFocus")
completeSession()
hero(destination: "List", type: .zoomOut)
}
#IBAction func allDoneTD(_ sender: Any) { allDone.tap(shape: .rectangle) }
#IBAction func focusAgainTU(_ sender: Any) {
UserDefaults.standard.set(currentFocusedStats + Int(timeToFocus), forKey: "UDFocusStats")
UserDefaults.standard.set(true, forKey: "UDShouldStartFocus")
initFocus()
}
#IBAction func focusAgainTD(_ sender: Any) { focusAgain.tap(shape: .rectangle) }
// MARK: Class Functions
#objc func initFocus() {
var ticker = 0.0
var angle = 0.0
var duration = 0.0
if UserDefaults.standard.bool(forKey: "UDShouldStartFocus") == true {
UserDefaults.standard.set(Date(), forKey: "UDFocusStartDate")
UserDefaults.standard.set(false, forKey: "UDShouldStartFocus")
ticker = timeToFocus
duration = timeToFocus
angle = 0.0
print("starting")
} else {
let elapsedTime = difference(between: UserDefaults.standard.object(forKey: "UDFocusStartDate") as! Date, and: Date())
let timeLeft = timeToFocus - elapsedTime
ticker = timeLeft
duration = timeLeft
angle = elapsedTime / (timeToFocus / 360)
}
// Timer
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
if ticker > 0 {
self.timeLabel.text = "\(Int(ticker))s"
ticker -= 1
}
}
timer.fire()
// Progress Circle
progress.animate(fromAngle: angle, toAngle: 360, duration: duration) { completed in
if completed { self.completeSession() }
}
// UI Changes
allDone.isHidden = true
focusAgain.isHidden = true
help.isHidden = false
}
func completeSession() {
// The timer gets fired every time, but this will invalidate it if it's complete
timer.invalidate()
timeLabel.text = "Done"
help.isHidden = true
allDone.isHidden = false
focusAgain.isHidden = false
}
// MARK: viewDidLoad
override func viewDidLoad() {
initFocus()
allDone.isHidden = true
focusAgain.isHidden = true
if timeToFocus < 3600 { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60)) minutes" }
else if timeToFocus == 3600 { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60/60)) hour" }
else { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60/60)) hours" }
stepNameLabel.text = UserDefaults.standard.string(forKey: "UDSelectedStep")
// This resumes the timer when the user sent the app in the background.
NotificationCenter.default.addObserver(self, selector: #selector(self.initFocus), name: NSNotification.Name(rawValue: "WillEnterForeground"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.fadeProgress), name: NSNotification.Name(rawValue: "WillEnterForeground"), object: nil)
}
#objc func fadeProgress(){
// This function is called both when the view will enter foreground (for waking the phone or switching from another app) and on viewWillAppear (for starting the app fresh). It will fade the progress circle and buttons to hide a flicker that occurs.
timeLabel.alpha = 0
dottedCircle.alpha = 0
progress.alpha = 0
allDone.alpha = 0
focusAgain.alpha = 0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
UIButton.animate(withDuration: 0.5, animations: {
self.timeLabel.alpha = 1
self.dottedCircle.alpha = 1
self.progress.alpha = 1
self.allDone.alpha = 1
self.focusAgain.alpha = 1
})
})
}
// MARK: viewWillAppear
override func viewWillAppear(_ animated: Bool) { fadeProgress() }
}
It seems the problem is that you create a local timer variable inside initFocus() but you call invalidate inside completeSession for another timer defined there:
class Focus: UIViewController {
// MARK: Variables
var timer = Timer()

timer doesn't work in cocoa/mac OS in Swift 4

I cant see why this time doesnt update. Been looking the entire day. I don't know if is something related to being my first MacOs project, and there's maybe something that's escaping me, but I'd love some help.
import Cocoa
class TextViewController: NSViewController {
#IBOutlet weak var text: NSScrollView!
#IBOutlet weak var dreadline: NSTextField!
var seconds: Int = 60
var timer: Timer?
var theWork = Dreadline(email: "", worktime: 0)
override func viewDidLoad() {
super.viewDidLoad()
print(seconds)
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print(self.seconds)
self.updateTimer()
} // this is the timer that doesn't work no matter what I try :(
}
#objc func updateTimer() {
if seconds < 1 {
timer?.invalidate()
} else {
seconds -= 1 //This will decrement(count down)the seconds.
dreadline.stringValue = "Dreadline: " + timeString(time: TimeInterval(seconds)) //This will update the label.
}
}
A very common mistake: You are creating a local timer which is not the same as the declared property.
Replace
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print(self.seconds)
self.updateTimer()
} // this is the timer that doesn't work no matter what I try :(
with
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print(self.seconds)
self.updateTimer()
} // this is the timer that doesn't work no matter what I try :(
The self before timer is actually not mandatory.
And set the timer to nil after invalidation to avoid a retain cycle
if seconds < 1 {
timer?.invalidate()
timer = nil
}
On the other hand you can use the local timer by deleting the property and change the code to
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print(self.seconds)
self.updateTimer(timer)
}
func updateTimer(_ timer : Timer) {
if seconds < 1 {
timer.invalidate()
} else {
seconds -= 1 //This will decrement(count down)the seconds.
dreadline.stringValue = "Dreadline: " + timeString(time: TimeInterval(seconds)) //This will update the label.
}
}

updating label variables from a running timer

I'm trying to create a counter that does simple math when the timer reaches zero. I have my timer working somewhat correctly, and the labels displaying the original variables, but they are not updating as the timer hits "zero". Where am I going wrong in my code?
class ViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var goldCounter: UILabel!
#IBOutlet weak var turnCounter: UILabel!
var seconds = 15
var timer = Timer()
var gold = 1000
var turns = 1
func updatelabels () {
goldCounter.text = String(gold)
turnCounter.text = String(turns) }
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer),userInfo: nil, repeats: true)
}
#objc func updateTimer() {
seconds -= 1
timerLabel.text = String(seconds)
if seconds == 0 {
seconds = 15}
}
func increaseGold () {
if seconds == 1 {
gold = gold + 1000
turns = turns + 1
}}
override func viewDidLoad() {
super.viewDidLoad()
self.runTimer()
goldCounter.text = String(gold)
turnCounter.text = String(turns)
// Do any additional setup after loading the view, typically from a nib.
func increaseGold () {
if seconds == 1 {
gold = gold + 1000
turns = turns + 1
}}
func updatelabels () {
goldCounter.text = String(gold)
turnCounter.text = String(turns) }
}
}
Your main issue is that you aren't calling updateLabels to update your labels.
I would suggest using property observers (didSet) to set your labels as the values change instead of relying on a separate function to change them.
Also, you need to call increaseGold in your timer handler:
class ViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var goldCounter: UILabel!
#IBOutlet weak var turnCounter: UILabel!
var seconds = 15 {
didSet {
timerLabel.text = String(seconds)
}
}
var timer = Timer()
var gold = 1000 {
didSet {
goldCounter.text = String(gold)
}
}
var turns = 1 {
didSet {
turnCounter.text = String(turns)
}
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer),userInfo: nil, repeats: true)
}
#objc func updateTimer() {
seconds -= 1
if seconds == 0 {
seconds = 15
}
increaseGold()
}
func increaseGold () {
if seconds == 1 {
gold = gold + 1000
turns = turns + 1
}
}
override func viewDidLoad() {
super.viewDidLoad()
seconds = 15
gold = 1000
turns = 1
self.runTimer()
}
}
I think when you added let, your problem is gone. Delete var timer = Timer(). It is not neccessary
func runTimer() {
let timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer),userInfo: nil, repeats: true)
}

Timer scope issue with #selector

I'm stuck with getting this into the right scope. I'm sure its something super simple but I'm banging my head against a wall with it. any answers i'm finding are in earlier version of swift so im struggling to understand how to solve this
My current issue is trying to get the timer initialised correctly and counting. the "selector" is causing the most issues. the rest i'm sure ill be able to figure out afterwards
code is as follows.
#IBOutlet weak var shortTimerLabel: UILabel!
#IBOutlet weak var longTimerLabel: UILabel!
var seconds = 60 //This variable will hold a starting value of seconds. It could be any amount above 0.
var timer = Timer()
var isTimerRunning = false //This will be used to make sure only one timer is created at a time.
#IBAction func longpressed(_ gestureRecognizer: UILongPressGestureRecognizer) {
shortTimerLabel.text = "longPressed"
}
#IBAction func tappedShortTimer(_ gestureRecognizer: UITapGestureRecognizer) {
shortTimerLabel.text = "ShortPressed"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}
func updateTimer() {
seconds += 1 //This will decrement(count down)the seconds.
shortTimerLabel.text = "\(seconds)" //This will update the label.
}
}
im trying to create a stopwatch which can be controlled using gestures. short pressed on label for stop/start and long press to reset time.
In your updateTimer() method, the first line should read seconds -= 1 instead (if you want to count down).
Also, you may want to update your updateTimer() method like this:
func updateTimer() {
seconds -= 1
if seconds == 0 {
timer.invalidate()
isTimerRunning = false
}
shortTimerLabel.text = String(describing: seconds)
}
An other issue here is that you added your runTimer() and updateTimer() method to the wrong place. You should not add them inside your viewDidLoad method.
Your final code would look like this:
var seconds = 60
var timer = Timer()
var isTimerRunning = false
#IBAction func longpressed(_ gestureRecognizer: UILongPressGestureRecognizer) {
resetTimer()
}
#IBAction func tappedShortTimer(_ gestureRecognizer: UITapGestureRecognizer) {
stopStartTimer()
}
override func viewDidLoad() {
super.viewDidLoad()
// ...
}
func stopStartTimer() {
if !isTimerRunning {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
isTimerRunning = true
} else {
timer.invalidate()
isTimerRunning = false
}
}
func updateTimer() {
seconds -= 1
if seconds == 0 {
timer.invalidate()
isTimerRunning = false
}
shortTimerLabel.text = String(describing: seconds)
}
func resetTimer() {
if isTimerRunning {
seconds = 60
timer.invalidate()
isTimerRunning = false
stopStartTimer()
}
}
The selector should be given in the form #selector(ViewController.updateTimer)
You shouldn't declare functions in the viewDidLoad but outside
You only set the timer in the longpressed function
For stoping it is timer.invalidate()