I am trying to have my UILabel’s text automatically update in increments of 1 based on a looping Timer. The label is linked to a variable. I’m not sure if it matters but I am doing my UI programmatically using auto layout anchors.
I understand this is not working because the variable lives outside of ViewDidLoad(). I also tried setting up a subclass of a UILabel in a separate file but I could not figure out the proper way to do that. I was having trouble connecting the variable to the subclass and properly implementing didSet.
Here is the relevant code from my View Controller, any recommendations or alternative methods are appreciated.
import UIKit
class ViewController: UIViewController {
var numberOfBreaths = 0
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let breathCounter = UILabel()
breathCounter.translatesAutoresizingMaskIntoConstraints = false
breathCounter.text = "(numberOfBreaths)"
breathCounter.textAlignment = .center
breathCounter.center = self.view.center
// Irrelevant hidden label code redacted
let startStop = RoundButton()
startStop.translatesAutoresizingMaskIntoConstraints = false
startStop.backgroundColor = .white
startStop.setTitle("breathe", for: .normal)
startStop.setTitleColor(.darkGray , for: .normal)
startStop.layer.borderWidth = 2.5
startStop.layer.borderColor = CGColor(red: 225, green: 225, blue: 0, alpha: 1)
startStop.addTarget(self, action: #selector(self.breathCount), for: .touchUpInside)
view.addSubview(breathCounter)
view.addSubview(holdTimer)
view.addSubview(startStop)
// Anchor code redacted
}
@objc func breathCount(_ sender: RoundButton) {
print("Button Tapped")
createTimer()
}
func createTimer() {
_ = Timer.scheduledTimer(timeInterval: 3.5, target: self, selector: #selector(nextBreath), userInfo: nil, repeats: true)
}
@objc func nextBreath() {
numberOfBreaths += 1
breathCounter.text = "(numberOfBreaths)" // Error: Cannot find 'breathCounter' in scope
print(numberOfBreaths) // Prints expected number to console
}
}
2
Answers
If you declare
breathCounter
as a property on your view controller (like you did fornumberOfBreaths
, you will have access to it from both theviewDidLoad
andnextBreath
functions. I’d also hold a reference to yourTimer
And then inside
viewDidLoad
, remove the existinglet breathCounter = UILabel()
line.And inside
createTimer
:You error message:
// Error: Cannot find 'breathCounter' in scope
gives a good clue… it’s all about scope.You declare your
UILabel
within theViewDidLoad()
method and so that’s where it lives; that’s its scope. As soon asViewDidLoad
completes, *poof *UILabel
disappears from memory.What you need to do is move your
let breathCounter = UILabel()
outside of ViewDidLoad so it gets created along with your ViewController; then you will be able to reference it as long as your ViewController exists in memory.