Can't get timer to count down - ios

I'm trying to learn how to use a timer in Swift, and every solution I look up is broken somehow or beyond my understanding.
I've tried with a closure, and without.
With a closure, I can actually get the app to run without crashing, but the timer just repeats 60, it doesn't count down and I don't know why.
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
var secondsRemaining = 60
print(secondsRemaining)
secondsRemaining -= 1
})
I've also tried using an #objc func with selector, but my app crashes right away with error Thread 1: "-[__SwiftValue countdown]: unrecognized selector sent to instance 0x600002970e40" (I haven't even gotten to trying the count down yet).
class ViewController: UIViewController {
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
#objc func countdown() {
print("fire")
}
…
…
}
Any help is appreciated.
EDIT
If I place my variable outside the block, I get an error inside the block Instance member 'secondsRemaining' cannot be used on type 'ViewController'
class ViewController: UIViewController {
var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
}
But if I create a new project and put the timer inside viewDidLoad(), it works. I don't know why.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
}
}

Every time your timer fires, a new variable with an initial value of 60 is instantiated, thus always printing 60.
You have to declare your counting variable outside:
private var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(self.secondsRemaining)
self.secondsRemaining -= 1
})
Or:
class ViewController: UIViewController {
private var secondsRemaining = 60
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
#objc func countdown() {
print("fire")
self.secondsRemaining -= 1
}
}

You're declaring counter inside the timer call. That's why every time the timer executes it resets the counter to 60 seconds. You need to declare your timer outside the it's call:
var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})

As mentioned in the comments above, don't reset the variable every single time you get in the closure and change you timer to the following. Create a simple obj function to do whatever logic you want. Also name your functionalists meaningfully
class XViewController: UIViewController {
private var secondsRemaining:Int = 60
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(startScrolling), userInfo: nil, repeats: true)
#objc func startScrolling() {
print(secondsRemaining)
secondsRemaining -= 1
}
}

Here is the fixed code:
class ViewController: UIViewController {
private var secondsRemaining = 60
override func viewDidLoad() {
super.viewDidLoad()
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
}
}

Related

Unexpected Output: I am using the below code to print out the timer according to the button pressed but timer is starting with 20 only. what's wrong?

import UIKit
class ViewController: UIViewController {
let eggTimes = ["Soft": 60,"Medium": 72,"Hard": 95]
var secondsRemaining = 20
var timer = Timer()
#IBAction func hardnessSelected(_ sender: UIButton) {
let hardness = sender.currentTitle!
var secondsRemaining = eggTimes[hardness]!
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
#objc func timerAction()
{
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds")
secondsRemaining -= 1
}
}
}
Unexpected Output: I am using the below code to print out the timer according to the button pressed but timer is starting with 20 only. what's wrong?
You are creating a new variable inside your Button function:
var secondsRemaining = eggTimes[hardness]!
instead you should assing your value. It should be:
self.secondsRemaining = eggTimes[hardness]!

How do I solve this error "Argument of '#selector' cannot refer to local function 'updateTimer()'", trying to make countdown timer in swift 5.1

import UIKit
class ViewController : UIViewController {
let eggTimes = ["Soft": 300, "Medium": 420, "Hard": 720]
var secondsRemaining = 60
#IBAction func hardnessSelected(_ sender: UIButton) {
let hardness = sender.currentTitle!
secondsRemaining = eggTimes[hardness]!
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
func updateTimer() {
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds.")
secondsRemaining -= 1
}
}
}
}
I am taking this Udemy course where it seems to be working fine but on my system, it is not working at all.
The action method must be on the top level of the class (the eggTimes level) and must be marked as #objc.
A more convenient solution is the (underestimated) closure-based API of Timer
Replace
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
func updateTimer() {
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds.")
secondsRemaining -= 1
}
}
with
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [unowned self] timer in
if self.secondsRemaining > 0 {
print("\(self.secondsRemaining) seconds.")
self.secondsRemaining -= 1
} else {
timer.invalidate()
}
}

Swift 4 Timer.invalidate() returns "Expression resolves to an unused function" error

I am making a game that has a timer displaying how long the current session has elapsed. I've followed a couple tutorials and read the documentation and can't seem to figure out why this is returning an error. Here is my code:
var timerT = Timer()
func startTimer () {
self.timerT = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timeFunc), userInfo: nil, repeats: true)
}
#objc func timeFunc (_ timer: Timer) {
timeS += 1
}
#IBAction func newGameAction(_ sender: UIButton) {
Timer.invalidate(timerT)
game = SetGame()
updateUIfromModel()
startTimer()
}
Much Mahalo.
You need to call invalidate() on an instance of a Timer, not on Timer itself.
Change:
Timer.invalidate(timerT)
to:
timerT.invalidate()

How to add countdowntimer which will run in whole iOS application?

I need to add a global timer of 60 minutes in the application which will show in all ViewControllers.
But not able to implement this feature.
I have implemented timer in one ViewController till now.
Here is my code:
var timerCount = 3600
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
func update() {
if(timerCount > 0){
let minutes = String(timerCount / 60)
let seconds = String(timerCount % 60)
timeLbl.text = minutes + ":" + seconds
timerCount -= 1
}
else {
timer.invalidate()
let vc = storyboard?.instantiateViewController(withIdentifier: "NavigateViewController")
navigationController?.pushViewController(vc!, animated: true)
}
}
I need to show this timer value in every ViewController of the application.
// Create class TimerManager
class TimerManager {
var timerCount = 3600
static let sharedInstance = TimerManager()
var timer: Timer?
init() {
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
if(timerCount > 0){
let minutes = String(timerCount / 60)
let seconds = String(timerCount % 60)
timerCount -= 1
}
else {
self.timer?.invalidate()
}
}
}
// At view Controller you can access timerCount
print(TimerManager.sharedInstance.timerCount)
You should display it in a separate UIWindow instance. Put the label in a separate View Controller and refer to answer 42 from this post:
To create a new UIWindow over the main window
// Create class TimerManager
class TimerManager {
var timerCount = 3600
static let sharedInstance = TimerManager()
var timer: Timer!
init() {
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
if(timerCount > 0){
let minutes = String(timerCount / 60)
let seconds = String(timerCount % 60)
timerCount -= 1
}
else {
self.timer?.invalidate()
}
}
}
// At view Controller you can access timerCount
print(TimerManager.sharedInstance.timerCount)

How to make count down timer with completion handler in swift

Currently I am trying to Create a 3...2...1... Go! type of count down where right after Go! is stated an action occurs however the option to use the completion handler for Timer.scheduleTimer doesn't have the ability to countdown from what I understand?
As of now I can count down from 3 but don't know how to invalidate the timer at Go! and perform an action
var seconds = 3
var countDownTimer = Timer()
func startCountDown() {
countDownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(TrackActivityVC.updateTimer), userInfo: nil, repeats: true)
}
func updateTimer() {
seconds -= 1
print(seconds)
}
// Not sure how to use this code to create a countdown, as it doesn't seem possible with this method
Timer.scheduledTimer(withTimeInterval: someInterval, repeats: false) {
}
Thus the question is How to make count down timer with completion handler.
Here is how to create a countdown timer using the trailing closure syntax:
var seconds = 3
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
self.seconds -= 1
if self.seconds == 0 {
print("Go!")
timer.invalidate()
} else {
print(self.seconds)
}
}
Here is a better version that invalidates the timer when the viewController is deinitialized. It uses [weak self] to avoid hanging on to the viewController if it closes:
class MyViewController: UIViewController {
var seconds = 3
var myTimer: Timer?
func startCountdown() {
myTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
self?.seconds -= 1
if self?.seconds == 0 {
print("Go!")
timer.invalidate()
} else if let seconds = self?.seconds {
print(seconds)
}
}
}
deinit {
// ViewController going away. Kill the timer.
myTimer?.invalidate()
}
}

Resources