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
}
}
CodePudding user response:
If you declare breathCounter
as a property on your view controller (like you did for numberOfBreaths
, you will have access to it from both the viewDidLoad
and nextBreath
functions. I'd also hold a reference to your Timer
class ViewController: UIViewController {
var numberOfBreaths = 0
let breathCounter = UILabel()
var timer : Timer?
And then inside viewDidLoad
, remove the existing let breathCounter = UILabel()
line.
And inside createTimer
:
self.timer = Timer.scheduledTimer(timeInterval: 3.5, target: self, selector: #selector(nextBreath), userInfo: nil, repeats: true)
CodePudding user response:
You error message: // Error: Cannot find 'breathCounter' in scope
gives a good clue... it's all about scope.
You declare your UILabel
within the ViewDidLoad()
method and so that's where it lives; that's its scope. As soon as ViewDidLoad
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.