ericathanas
ericathanas

Reputation: 223

UIBarButtonItem doesn't work when created as a property, but does when created in a function

I have a UIBarButtonItem in the right side of my navigation that has an image of a gear and presents my settings view controller. I can get it to work properly when I create the button in setupNavigationBar(), but it doesn't work if I create the button as a property. I can't wrap my head around what would be different about these two scenarios. The button is present in both situations, but the functionality isn't.

This version doesn't work

class DecksController: UIViewController {

    let settingsBarButton: UIBarButtonItem = {
        let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
        return barButton
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationBar()
    }

    @objc func presentSettings() {
        let settingsController = SettingsController()
        self.navigationController?.pushViewController(settingsController, animated: true)
    }

    func setupNavigationBar() {
        self.navigationItem.rightBarButtonItem = settingsBarButton
    }
}

This version does work

class DecksController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationBar()
    }

    @objc func presentSettings() {
        let settingsController = SettingsController()
        self.navigationController?.pushViewController(settingsController, animated: true)
    }

    func setupNavigationBar() {
        let settingsBarButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
        self.navigationItem.rightBarButtonItem = settingsBarButton
    }
}

Upvotes: 1

Views: 48

Answers (1)

matt
matt

Reputation: 535138

As you've discovered, it makes a big difference where this line occurs:

let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), 
    style: .plain, target: self, action: #selector(presentSettings))

The problem is the target:self part. When the bar button item is configured as part of an instance property initializer (your first example), the instance doesn't exist yet — it is what we are initializing. So self has no meaning, and the button ends up with no target. Therefore, tapping the button does nothing.

(Actually, to be quite technical, self is the class, but that's not a helpful thing to know.)

In your second example, that line is part of viewDidLoad, which runs considerably after the view controller instance has come into existence and has been initialized. viewDidLoad is an instance method, in fact. So self is the instance, as you expect.

Upvotes: 2

Related Questions