CodeBender
CodeBender

Reputation: 36610

UIBarButtonItem added programatically to nav bar fails to call selector, while button added to toolbar does

I have a somewhat bewildering problem. In my app, I have a toolbar that can swap out buttons programmatically and properly call the associated selector. However, when I apply the same technique to a button on the navigation bar, it fails to call the selector. The following sample code contains 2 sets of buttons (one for nav & one for toolbar). Despite being identical, the selector on the nav bar button is never called.

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var toolbar: UIToolbar!

    let navCancelButton = UIBarButtonItem(barButtonSystemItem: .cancel,
                                          target: self,
                                          action: #selector(toggleDelete))
    let navTrashButton = UIBarButtonItem(barButtonSystemItem: .trash,
                                         target: self,
                                         action: #selector(toggleDelete))

    let toolbarCancelButton = UIBarButtonItem(barButtonSystemItem: .cancel,
                                              target: self,
                                              action: #selector(toggleDelete))
    let toolbarTrashButton = UIBarButtonItem(barButtonSystemItem: .trash,
                                             target: self,
                                             action: #selector(toggleDelete))

    var isDeleting = false {
        didSet {
            navigationItem.rightBarButtonItem = isDeleting ? navCancelButton : navTrashButton
            toolbar.items = isDeleting ? [toolbarCancelButton] : [toolbarTrashButton]
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        isDeleting = false
    }

    @objc func toggleDelete() {
        isDeleting = !isDeleting
    }
}

This shows the previous code in action:

enter image description here

Upvotes: 1

Views: 417

Answers (2)

mugx
mugx

Reputation: 10105

The fact is the target is being lost, it might be a "bug", a possible workaround is using the lazy var

lazy var navCancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self,  action: #selector(toggleDelete))
lazy var navTrashButton = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action: #selector(toggleDelete))

Upvotes: 4

Upholder Of Truth
Upholder Of Truth

Reputation: 4711

It appears that the buttons in the nav bar lose their target. So you can either set the target in the viewDidLoad like this:

self.navTrashButton.target = self
self.navCancelButton.target = self

or what I would do is just put the variables in the class scope like this:

var navCancelButton: UIBarButtonItem!
var navTrashButton: UIBarButtonItem!

and then create them in viewDidLoad like this:

navCancelButton = UIBarButtonItem(barButtonSystemItem: .cancel,
                                  target: self,
                                  action: #selector(toggleDelete))
navTrashButton = UIBarButtonItem(barButtonSystemItem: .trash,
                                 target: self,
                                 action: #selector(toggleDelete))

That way you know the buttons are being created when the view controller is expecting them to be and ready for them.

Upvotes: 5

Related Questions