codeDerek
codeDerek

Reputation: 21

Updating UILabel when timer counts down each second in Swift 4.2

New guy here teaching myself Swift. Building my first personal app and have hit a wall after several searches on here, youtube, and google. This is my first time posting a question (as I've been able to find my other answers on here).

I'm having issues with my timer updating on the UILabel. I've managed to find code on older versions of swift that get the timer to run and count down. I then figured out how to break the seconds down to minutes and seconds.

But what I find is when I run the app, the timer shows "30:0" (a different issue I need to figure out) and never counts down. When I leave the page in the simulator and come back, it's only then that the UILabel updates.

I know viewdidload only loads upon the first moment the page opens. I'm having a hard time figuring out how to get the UILabel to update every time a second changes. I'm not sure what code to implement.

Thank you so much!

import UIKit

var timer = Timer()
var timerDuration: Int = 1800

// This converts my timeDuration from seconds to minutes and seconds.
func secondsToHoursMinutesSeconds (seconds : Int) -> (h: Int, m : Int, s : Int) {
    return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}

class lyricWriteViewController: UIViewController {
    //This takes the function to convert minutes and seconds and accepts an input, which I've chosen the variable timeDuration (which is currently 1800 seconds.
    var theTimer = (h: 0, m: 0, s: 0)

    @IBOutlet weak var countdownTimer: UILabel!
    @IBOutlet weak var randomLyric: UILabel!
    @IBOutlet weak var titleInput: UITextField!
    @IBOutlet weak var lyricInput: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        //This line takes a random array number and shows it on the textlabel.

        randomLyric.text = oneLiner
        theTimer = secondsToHoursMinutesSeconds(seconds: timerDuration)

        //This is the code the does the counting down
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(lyricWriteViewController.counter), userInfo: nil, repeats: true)
    }

    @objc func counter() {
        timerDuration -= 1

        // Below is the timer view I've created. It has converted seconds to minutes and seconds but the screen won't refresh. Also, when the seconds number hits zero, it does "0" instead of "00".
        let displayTimer = "\(theTimer.m) : \(theTimer.s)"
        countdownTimer.text = String(displayTimer)

        //When the timer hits 0, it stops working so that it doesn't go into negative numbers

        if timerDuration == 0 {
            timer.invalidate()
        }
    }

    func submitlyricsButton(_ sender: UIButton) {
        //I will eventually tie this to a completed lyric tableview.
    }
}

Upvotes: 2

Views: 3376

Answers (3)

Muhammad Jabbar
Muhammad Jabbar

Reputation: 61

var timer: Timer?
var totalTime = 120

private func startOtpTimer() {
    self.totalTime = 120
    self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}

@objc func updateTimer() {
    print(self.totalTime)
    self.lblTimer.text = self.timeFormatted(self.totalTime) // will show timer
    
    if totalTime != 0 {
        totalTime -= 1 // decrease counter timer
    } 
    else {
        if let timer = self.timer { 
            timer.invalidate()
            self.timer = nil
        }
    }
}

func timeFormatted(_ totalSeconds: Int) -> String {
    let seconds: Int = totalSeconds % 60
    let minutes: Int = (totalSeconds / 60) % 60
    return String(format: "%02d:%02d", minutes, seconds)
}

Upvotes: 6

iOSer
iOSer

Reputation: 2336

Replace countdownTimer.text = String(displayTimer) with

DispatchQueue.main.async {
    countdownTimer.text = String(displayTimer)
}

What I think is happening here is since countdownTimer.text = String(displayTimer) is not running on the main thread it is not updating immediately. It does however after a period of time (Like you said, when when you traverse the screen).

Upvotes: -1

Sharad Chauhan
Sharad Chauhan

Reputation: 4891

It is because you are not updating the theTimer value. As viewDidLoad() is called once it is not working fine, you need to update theTimer value after deducting 1 from it. So move this line :

theTimer = secondsToHoursMinutesSeconds(seconds: timerDuration)

in counter() funtion after timerDuration -= 1. So your function should look like this :

@objc func counter() {
    timerDuration -= 1
    if timerDuration == 0 {
        timer.invalidate()
    } else {
        theTimer = secondsToHoursMinutesSeconds(seconds: timerDuration)
        let displayTimer = "\(theTimer.m) : \(theTimer.s)"
        countdownTimer.text = String(displayTimer)
    }        
}

Also move all of this inside controller:

var timer = Timer()
var timerDuration: Int = 1800

// This converts my timeDuration from seconds to minutes and seconds.
func secondsToHoursMinutesSeconds (seconds : Int) -> (h: Int, m : Int, s : Int){
return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)}

As timerDuration is global you will have to kill the app and run it again to see the timer working again.

Upvotes: 1

Related Questions