Label not showing value of timer in Swift - ios

I have a timer loop that executes a basic countdown and it prints the value to the console. I'm trying to have that value set to a text value of a label. Even though the Xcode console shows the correct countdown of the timer value, the label in the application still shows 0. Any ideas as to why this is happening? Here is the relevant code:
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var timerLabel: UILabel!
var timerCount = 7
var timerRunning = false
var timer = NSTimer()
override func viewDidLoad() {
super.viewDidLoad()
self.timerCount = 7
self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Counting"), userInfo: nil, repeats: true)
}
func Counting(){
timerCount = 7
do {
println(timerCount)
timerRunning = true
--timerCount
timerLabel.text = "\(timerCount)"
println(timerCount)
} while timerCount > 0
}

The method Counting() is wrong.
Every second you are launching the counting method and within that method you have a loop which updates the timerLabel.text, but the UI is not updated until the Counting() finishes...that's why is always showing 0. You need just to decrease the counting every second and update the label.
I think this is what you need:
func Counting(){
if timerCount == 0
{
timerCount = 7 // or self.timer.invalidate() in case you want to stop it
}
else
{
timerCount--;
timerLabel.text = "\(timerCount)"
println(timerCount)
}
}
Hope it helps

Related

Timer Updating Label 1 Second Behind 2nd Timer Counting Down

I'm having difficulties attempting to link two timers together. I'm trying to have a timer count down from a specified amount and to have a second timer constantly updating a label on a view controller. However, I end up having the timer that updates the label lagging exactly 1 second behind the first timer in the timer class. Here's what I have for the view controller: (note that this is a condensed version of my code)
class HomeViewController: UIViewController {
#IBOutlet var timeLabel: UILabel!
override func viewDidLoad() {
timeLabel.text = account.deedManager.globalTimer.timerStr
Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(UpdateTime), userInfo: nil, repeats: true)
}
#objc func UpdateTime() {
self.timeLabel.text = account.deedManager.globalTimer.timerStr
}
}
And here is the Timer class:
class TimerModel: NSObject, NSCoding {
var myTimer: Timer? = Timer()
var timerInterval: TimeInterval = 1.0
var timerEnd: TimeInterval = 0.0
var timerCount: TimeInterval = 86400.0 // 24 hours
var timerStr: String = "TIME"
func StartTimer(time: Double) {
timerCount = time
myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UpdateTime), userInfo: nil, repeats: true)
}
#objc func UpdateTime() {
self.timerStr = self.TimerDate(time: self.timerCount)
self.timerCount-=1
print(self.timerStr)
}
func TimerDate(time:TimeInterval) -> String {
let hours = Int(time) / 3600
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
}
}
I've tried to make the first timer a 0.05 interval so that it updates more rapidly than the timer class, but it lags behind exactly a second no matter what interval I put it at. I don't want to put the count down timer inside the view controller as I want the timer global for all view controllers. If you have any ideas, let me know.
First you need to switch the order around so that you are setting the text after you decrement the timerCount:
#objc func UpdateTime() {
self.timerCount-=1
self.timerStr = self.TimerDate(time: self.timerCount)
print(self.timerStr)
}
Then, you can delete the 0.05-second timer because as you said, that doesn't seem to work.
Try using the delegate pattern instead.
protocol TimerModelDelegate {
func timerTextDidChange(_ timer: TimerModel, text: String)
}
And then in TimerModel,
weak var delegate: TimerModelDelegate?
var timerStr: String = "TIME" {
didSet {
delegate?.timerTextDidChange(self, text: timerStr)
}
}
In HomeViewController, do this:
class HomeViewController: UIViewController, TimerModelDelegate {
#IBOutlet var timeLabel: UILabel!
override func viewDidLoad() {
timeLabel.text = account.deedManager.globalTimer.timerStr
account.deedManager.globalTimer.delegate = self
account.deedManager.globalTimer.StartTimer(time: 60)
}
// You don't need the UpdateTime method here
func timerTextDidChange(_ timer: TimerModel, text: String) {
timeLabel.text = text
}
}
You have this function:
#objc func UpdateTime() {
self.timerStr = self.TimerDate(time: self.timerCount)
self.timerCount-=1
print(self.timerStr)
}
Replace it with
#objc func UpdateTime() {
self.timerCount-=1
self.timerStr = self.TimerDate(time: self.timerCount)
print(self.timerStr)
}
Explanation:
You first have to change the value, then display it
Notice the first two lines are swapped. This should fix your issue.

All digits not shown in timer countdown

While coming to a view I call a function to load a timer like so...
var count = 10
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
and update function is given as..
#objc func update() {
while (count != 0) {
count -= 1
countdownLabel.text = "\(count)"
}
timer.invalidate()
}
But what happens is when I come to this view, straightaway the number 0 is shown as opposed to ideally displaying all numbers in the sequence 9,8,7,6,5,4,3,2,1,0
What am I doing wrong here..?
Swift 4:
var totalTime = 10
var countdownTimer: Timer!
#IBOutlet weak var timeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
startTimer()
}
This method call initializes the timer. It specifies the timeInterval (how often the a method will be called) and the selector (the method being called).
The interval is measured seconds so for it to perform like a standard clock we should set this argument to 1.
func startTimer() {
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
// Stops the timer from ever firing again and requests its removal from its run loop.
func endTimer() {
countdownTimer.invalidate()
}
//updateTimer is the name of the method that will be called at each second. This method will update the label
#objc func updateTime() {
timeLabel.text = "\(totalTime)"
if totalTime != 0 {
totalTime -= 1
} else {
endTimer()
}
}

How Do I create a countdown timer with minutes and seconds in swift?

I can't find a single tutorial that does countdown timer with minutes and seconds. There's one or two but they are kind of bad.
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var focusSession: UILabel!
#IBOutlet weak var breakSession: UILabel!
var prodSeconds = String() // This value is set in a different view controller
lazy var intProdSeconds = Int(prodSeconds)
var timer = Timer()
var isTimerRunning = false // Make sure only one timer is running at a time
override func viewDidLoad() {
super.viewDidLoad()
if isTimerRunning == false {
runProdTimer()
}
//focusSession.text = String(prodMinutes) + ":" + String(prodSeconds) // Ignore this for now stack overflow ppl
}
func runProdTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(HomeViewController.updateProdTimer)), userInfo: nil, repeats: true)
isTimerRunning = true
}
#objc func updateProdTimer() {
if intProdSeconds! < 1 {
timer.invalidate()
focusSession.text = "00:00"
}
else {
intProdSeconds! -= 1
focusSession.text = prodTimeString(time: TimeInterval(prodSeconds)!)
}
}
func prodTimeString(time: TimeInterval) -> String {
let prodMinutes = Int(time) / 60 % 60
let prodSeconds = Int(time) % 60
return String(format: "%02d:%02d", prodMinutes, prodSeconds)
}
}
The user inputs their time amount and it is stored in the prodSeconds variable which is then converted to an Int below it with the lazy variable.
However, the timer still doesn't countdown when I run the app.
This is supposedly just a timer for seconds which I was following from a different tutorial. But all that happens is that the label that displays the timer simply displays the number inputted by the user in the format of 00:prodSeconds and doesn't actually countdown.
P.S. Don't worry about implementing a start/stop button for now. In my case, the timer is supposed to start when the view loads.
The problem is that you count down from
intProdSeconds! -= 1
and pass prodSeconds to this
focusSession.text = prodTimeString(time: TimeInterval(prodSeconds)!)
so make sure to deal only with intProdSeconds

Disable timer button after validation Swift - Not working

looking for some help for this, I have a timer that works after submitting a password which is great, but I then need to disable the button after the timer starts and is disabled for a period of time, (in the code I have entered a nominal 90 seconds)
however the button is not disabling.
if anybody could show me where I am going wrong that would be awesome.
import UIKit
class appiHour: UIViewController {
var timer = Timer()
var counter = 60
var password_Text: UITextField?
func enableButton() {
self.timerStartButton.isEnabled = true
}
#IBOutlet weak var timerLabel: UILabel!
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.
}
#IBAction func timerStartButton(_ sender: Any) {
var password_Text: UITextField?
let alertController = UIAlertController(title: "To start your own 2 Cocktails for £10 APPi Hour", message: "get a memeber of the team to enter the password, but use it wisely, as you can only use it once per day, with remember great power comes great responsability", preferredStyle: UIAlertControllerStyle.alert)
let tickoff_action = UIAlertAction(title: "let the APPiness commence", style: UIAlertActionStyle.default) {
action -> Void in
self.timerStartButton.isEnabled = false
Timer.scheduledTimer(timeInterval: 90, target: self, selector: #selector(appiHour.enableButton), userInfo: nil, repeats: false)
if let password = password_Text?.text{
print("password = \(password)")
if password == "baruba151" {
self.counter = 60
self.timerLabel.text = String(self.counter)
self.timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(appiHour.updateCounter), userInfo: nil, repeats: true)
}
} else {
print("No password entered")
}
}
alertController.addTextField { (txtpassword) -> Void in
password_Text = txtpassword
password_Text!.isSecureTextEntry = true
password_Text!.placeholder = ""
}
alertController.addAction(tickoff_action)
self.present(alertController, animated: true, completion: nil)
}
#IBOutlet weak var timerStartButton: UIButton!
func updateCounter() {
counter -= 1
timerLabel.text = String(counter)
if counter == 0{
timer.invalidate()
counter = 0
}
}
}
As a secondary question is it possible to run the timer while the app is in the background? i know apple frowns on this aside for Sat Nav, Music apps etc. But is there a method in which the timer is held and a notification is sent locally letting the user know the timer has ended?
thanks in advance.
I suspect that your action may not be hooked up to your button. I just tried the following code with no issues. The button gets disabled, and then enabled 5 seconds later:
class ViewController: UIViewController {
#IBOutlet weak var myButton: UIButton!
#IBAction func ButtonPressed(_ sender: Any) {
myButton.isEnabled = false
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(myTimerTick), userInfo: nil, repeats: false)
}
func myTimerTick() {
myButton.isEnabled = true
}
}
So make sure your outlets and actions are hooked up to the button correctly. If you right click on your button, you should see the dots filled in next to the outlet and action. You should see similarly filled in dots in your code.
You can further verify it is hooked up by placing a breakpoint in your "timerStartButton" method and making sure that breakpoint is hit.
Edit to further clarify: You need to connect your code to your Interface build objects. See this article from Apple for a complete tutorial on how to do that.
I'm not 100% sure if this is what you mean. But this would at least satisfy the first part of your request: disable a button whilst a timer is running, and re-enable it once the timer stops.
#IBOutlet weak var myButton: UIButton!
#IBOutlet weak var timerCount: UILabel!
#IBAction func buttonPressed(_ sender: UIButton) {
var count = 0
sender.isEnabled = false
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [unowned self] timer in
count += 1
if count == 5 {
count = 0
sender.isEnabled = true
timer.invalidate()
}
self.timerCount.text = "\(count)"
}
}
Here's a couple of screenshots of what you get.
It's enabled when the user starts off, disabled whilst the count is going then reverts back to its original state with counter at 0 and button enabled. Is that what you're going for?
As far as your second question, what do you mean by
the timer is held
Do you want the timer to keep running whilst the app is in the background, then update the user once the timer has elapsed? If so, take a look at this answer which should point you in the right direction: Continue countdown timer when app is running in background/suspended

What would cause my segue to infinitely loop?

I've implemented a count-down timer that will automatically start my application if the user doesn't select any options. When the timer hits zero, I invalidate it and fire performSegueWithIdentifier, which segues me to my desired view.
At that point all is fine... well, sort of. I do notice that my view fires twice, but its fine after that. At this point, if I navigate away from that view, then back again, my segue fires and the view loads over and over until I stop my app.
my output window shows:
2015-05-13 21:20:26.880 Web App Browser[43407:7957566] Unbalanced
calls to begin/end appearance transitions for
. 2015-05-13
21:20:28.825 Web App Browser[43407:7957566] Unbalanced calls to
begin/end appearance transitions for .
Here's my view controller:
class StartViewController: UIViewController {
var countDown = Bool()
var timer = NSTimer()
var count = 5
#IBOutlet weak var countdownLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
countDown = AppDelegate().userDefaults.valueForKey("Auto Start") as! Bool
if countDown == true {
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
} else {
countdownLabel.text = ""
}
}
func update() {
countdownLabel.text = "\(count)"
if count == 0 {
timer.invalidate()
self.performSegueWithIdentifier("toWeb", sender: nil)
} else {
count--
}
}
}
my storyboard:
In the image below, you see my selected segue, which takes the user from the start screen into a navigation controller that has an embedded viewController. You'll note that I've added my Identifier as "toWeb".
My Question:
What would cause my segue to infinitely loop?
Not sure if this is directly related to your issue, but you are declaring timer twice, once locally and once at class scope.
var countDown = Bool()
var timer = NSTimer()
var count = 5
#IBOutlet weak var countdownLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
countDown = AppDelegate().userDefaults.valueForKey("Auto Start") as! Bool
if countDown == true {
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
} else {
countdownLabel.text = ""
}
}
you see the var timer = NSTimer() creates a timer at class scope
var timer = NSTimer.scheduleTimerWithTimeInterval... creates a new timer in the scope of viewDidLoad. I assume that should just be timer = NSTimer.scheduleTimer...
I suppose this was pretty obvious, but my update was getting called every second... because i told it to. And I put my performSegueWithIdentifier inside it. So, easy fix.
var segueFlag = false
func update() {
countdownLabel.text = "\(count)"
if count == 0 {
timer.invalidate()
if segueFlag == false {
self.performSegueWithIdentifier("toWeb", sender: nil)
segueFlag = true
}
} else {
count--
}
}

Resources