Unable to stop the Timer by changing the Boolean value to false in Swift - swift5

I tried running the following code but the timer won't stop after counting till "0" seconds.
Desired OutPut : 5 seconds. 4 seconds. 3 seconds. 2 seconds. 1
seconds. 0 seconds.
class ViewController: UIViewController {
var seconds = 5
var value = true
#IBAction
func buttonPressed(_ sender: UIButton) {
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(secondsRemaining), userInfo: nil, repeats: value)
}
#objc
func secondsRemaining() {
print("\(seconds) seconds.")
if seconds > 0{
seconds -= 1
}else{
value = false
}
}
}

When you schedule a timer passing true as repeats, that means that this timer will continue to occur unless you explicitly call invalidate() function on the returning instance and set it to nil.
So, your code should be something like this:
class ViewController: UIViewController {
var seconds = 5
var timer: Timer?
#IBAction func buttonPressed(_ sender: UIButton) {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(secondsRemaining), userInfo: nil, repeats: true)
}
#objc func secondsRemaining() {
print("\(seconds) seconds.")
if seconds > 0 {
seconds -= 1
} else {
timer?.invalidate()
timer = nil
}
}
}

Related

How to print a value in loop after some delay in swift

i want to print my values in loop after 5 seconds delay
here is how im trying to do so
let count = 1...10
for calls in count{
let seconds = 5
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(seconds), execute: {
print(calls)
})
}
But it waits 5 seconds only in the first time and the print all the values at once.
Maybe there is another way to call a function many times after a delay of time please recommened
this is how im trying with timer
var seconds = 10
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
let count = 1...10
for number in count{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.counter), userInfo: nil, repeats: true)
print(number)
}
}
#objc func counter(){
seconds -= 1
if (seconds == 0){
print("User Location")
}
}
}
Two pieces of criticisms. Forst, don't use a loop - it's executed immediately. Second, do use threading, there's no need. Use a Timer instead:
class ViewController: UIViewController {
var timer = Timer()
var iterations = 0
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(printTime), userInfo: nil, repeats: true)
}
#objc func printTime() {
print(Date())
iterations += 1
if iterations == 5 {
timer.invalidate()
}
}
}
It's output:
2020-08-15 18:08:38 +0000
2020-08-15 18:08:43 +0000
2020-08-15 18:08:48 +0000
2020-08-15 18:08:53 +0000
2020-08-15 18:08:58 +0000
After 5 iterations, you invalidate the time to shut it off.

Convert seconds to Minutes:Seconds in label MacOS

I am a school teacher and we have been 'ordered' to use a specific timer for our lessons, which low and behold doesn't work on our apple iMac's. I am trying to create my own in xcode and so far have created a basic window which will countdown a label in seconds. I have at the moment assigned the buttons and they work (in increments of 60 seconds).
This works and is fine but ideally i would like the label to display minutes and seconds instead (much easier for the kids). What is the best way to code this? Last time i used xcode was in 2009 and i am way out of date now!! Thanks in advance
--
#objc func updateTimer() {
seconds -= 1 //This will decrement(count down)the seconds.
countdownLabel.stringValue = "\(seconds)" //This will update the label.
}
--
#objc func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector:(#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
}
--
#IBAction func threeMin(_ sender: Any) {
seconds = 180
runTimer()
}
--
There are many solutions. A convenient one is DateComponentsFormatter
let formatter : DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.minute, .second]
return formatter
}()
#objc func updateTimer() {
seconds -= 1
countdownLabel.stringValue = formatter.string(from: TimeInterval(seconds))!
}
Some improvements:
Assign tags to all buttons with their value in seconds for example set the tag of the threeMin button to 180. Then use only one IBAction and connect all buttons to that action.
In the action first check if the timer is running and start it only if it's not running
var timer : Timer?
#IBAction func startTimer(_ sender: NSButton) {
if timer == nil {
seconds = sender.tag
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.updateTimer), userInfo: nil, repeats: true)
}
}
Create a function to stop the timer reliably
func stopTimer() {
if timer != nil {
timer?.invalidate()
timer = nil
}
}
In the updateTimer() function stop the timer if seconds is 0
#objc func updateTimer() {
seconds -= 1
countdownLabel.stringValue = formatter.string(from: TimeInterval(seconds))!
if seconds == 0 { stopTimer() }
}

Countdown speeds up when the button that starts it is pressed multiple times

I just started to learn swift . and I found when using a button to start a count down if i pressed the button twice it speeds up the process. What to add to prevent that?
#IBAction func startButton(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(processTime), userInfo: nil, repeats: true)
}
#objc func processTime(){
if counter > 0 {
counter -= 1
timeLabel.text = "\(counter)"
}else {
timer.invalidate()
}
}
I tried to use sender.isEnabled = false it gave this error (Value of type 'Any' has no member 'isEnabled')
so I did it like this :
#IBAction func startButton(_ sender: Any) {
if timer.isValid != true{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(processTime), userInfo: nil, repeats: true)
}
}
Many ways, depends on what logic you prefer.
Basically you need to ensure that the timer starts only once until it completes.
In the following example, we start the timer only if it's not been initialized previously.
Furthermore, when we stop the timer, we explicitly set it to nil so the following logic works again after the timer completes.
//globally declared as optional
var timer: Timer?
#IBAction func startButton(_ sender: Any) {
//check if timer is nil
if timer != nil {
//start timer only if timer is nil
timer = Timer.scheduledTimer(timeInterval: 1,
target: self,
selector: #selector(processTime),
userInfo: nil,
repeats: true)
}
}
#objc func processTime() {
if counter > 0 {
counter -= 1
timeLabel.text = "\(counter)"
}
else {
timer.invalidate()
//clear the timer
timer = nil
}
}
add a sender.isEnabled = false and after you press the button once it won't be clickable again
You need to invalidate the timer by calling timer.invalidate() before you reassign a new timer. If you assign a new timer instance to the old one without invalidating, you will lose reference to it and it will never be invalidated.

How to count Timer on Swift

I'm a newbie in swift and I tried to make a Timer. It should normally count the seconds and print them in the debugger.
I tried this:
var timer = Timer()
#IBAction func killTimer(_ sender: AnyObject) {
timer.invalidate()
}
#objc func processTimer() {
print("This is a second")
}
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
}
I don't know how the timer count seconds.. With this code i get an fail message:
#objc func processTimer() {
print("This is second \(Timer + 1)")
}
Thanks for your help.
A
You need a counter variable which is incremented every time the timer fires.
Declare the variable Timer as optional to invalidate the timer reliably (only once).
var timer : Timer?
var counter = 0
#IBAction func killTimer(_ sender: AnyObject) {
timer?.invalidate()
timer = nil
}
#objc func prozessTimer() {
counter += 1
print("This is a second ", counter)
}
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval:1, target:self, selector:#selector(prozessTimer), userInfo: nil, repeats: true)
}
You need to start your timer, you have only initialised it in your code so in viewDidLoad add
timer.fire()
I am not sure what you want to print but if it is a timestamp then you could do add a property to your class
let formatter = DateFormatter()
and configure it to show time in seconds and milliseconds
formatter.dateFormat = "ss.SSS" //in viewDidLoad
and use it in your print statement
print("This is a second \(formatter.string(from(Date())")
If you want to print the time that passed use this (If that's what you want):
print("time passed: \(Date().timeIntervalSince1970 - timer.fireDate.timeIntervalSince1970)")

Timer counts instantly instead of respecting interval

Im trying to make a countdown timer. everything works fine except that my timer does not count at regular intervals (1sec); instead it counts all the way down instantly giving me 0 every time. did a lot of search without luck. All examples I could find show similar timeInterval parameter.
var timer = Timer()
var remainingTime = 120
#objc func timerCount () {
if remainingTime > 0 {
while remainingTime > 0 {
remainingTime -= 1
timerLabel.text = String(remainingTime)
print(remainingTime)
}
} else {
timer.invalidate()
}
}
#IBAction func pauseButton(_ sender: Any) {
timer.invalidate()
}
#IBAction func playButton(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerCount), userInfo: nil, repeats: true)
}
The reason your code is not working is that you have put an unnecessary while loop in your timerCount() method. You don't need to do this. Your timer will fire this method after each time interval. At very first call this while loop make your remainingTime to 0. This is why you are instantly getting 0 every time.
You just need to remove that while loop.
Can You try like this
var timer = Timer()
var remainingTime = 120
#objc func timerCount () {
let date = NSDate()
let nowdate = UserDefaults.standard.object(forKey: "NowDate") as! Date
let miniute = nowdate.timeIntervalSince(date as Date)
print(Int(miniute))
if (Int(miniute) == 0) {
timer.invalidate()
UserDefaults.standard.removeObject(forKey: "NowDate")
} else {
timerLabel.text = String(Int(miniute))
}
}
#IBAction func pauseButton(_ sender: Any) {
timer.invalidate()
UserDefaults.standard.removeObject(forKey: "NowDate")
}
#IBAction func playButton(_ sender: Any) {
let CurrentDate = NSDate()
let NowDate = CurrentDate.addingTimeInterval(TimeInterval(remainingTime))
UserDefaults.standard.set(NowDate, forKey: "NowDate")
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerCount), userInfo: nil, repeats: true)
}