Search code examples
swiftuikituilabelselectorviewdidload

Defining a #selector method when it can't access a programmatically created UILabel


If you define a UILabel programmatically, it must be done so in viewDidLoad()? But, if you were to define a #selector method that needed to set said label, it would be impossible given that the @objc selector method needs to be outside the scope of the viewDidLoad() method, where it cannot access the label. How do you get around this?

class ViewController: UIViewController {
        
    var count = 4
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
                
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
        label.center = CGPoint(x: 250, y: 430)
        label.font = .systemFont(ofSize: 130)
        label.textColor = UIColor.black
        view.addSubview(label)
        
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: <#T##Selector#>, userInfo: <#T##Any?#>, repeats: <#T##Bool#>)
        
        
        @objc func updateLabel() { // ERROR @objc can only be used with members of classes...
            if count > 0 {
                count -= 1
                label.text = "\(count)"
            }
        }
    }
}

Solution

  • Your mistake is that you have put the selector updateLabel inside of viewDidLoad. You should put it outside viewDidLoad so that it is a member of your class, rather than a local function.

    To access the label in updateLabel, simply move the declaration of the label outside of both methods.

    You should also add a timer parameter to updateLabel so that you can stop the timer when count reaches 0, rather than keeping the timer running forever.

    var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        label = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
        label.center = CGPoint(x: 250, y: 430)
        label.font = .systemFont(ofSize: 130)
        label.textColor = UIColor.black
        view.addSubview(label)
        
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateLabel), userInfo: nil, repeats: true)
    }
    
    @objc func updateLabel(_ timer: Timer) {
        if count > 0 {
            count -= 1
            label.text = "\(count)"
        } else {
           timer.invalidate()
        }
    }