Eli Byers
Eli Byers

Reputation: 145

Add tap action to custom view in NavigationController

I am trying to add an action to my custom view in my navigation bar. I have it showing up fine, but I can't figure out how to add a tap action to it.

I have tried adding a button inside the view and handling it there. I have tried making my navigation controller a delegate of the custom view, I have tried adding a tap gesture recognizer to the view in the nav controller. Nothing has worked. Any advice or feedback is greatly appreciated.

Thanks!

My custom Navigation Controller:

class MainNavVC: UINavigationController {

    var loadStatus = LoadStatus()

    override func viewDidLoad() {
        super.viewDidLoad()

        // load status
        loadStatus.bounds = CGRect(x: -24, y: -6, width: 0, height: 0)
        let loadStatusButton = UIBarButtonItem(customView: loadStatus)
        self.viewControllers.last?.navigationItem.leftBarButtonItem = loadStatusButton

        let tap = UITapGestureRecognizer(target: self, action: #selector(loadStatusPressed))
        loadStatus.addGestureRecognizer(tap)
        loadStatus.isUserInteractionEnabled = true
    }

    @objc func loadStatusPressed(recognizer: UIGestureRecognizer) {
        print("tapped")
        let alert = UIAlertController(title: "Load Status", message: "When you are under a load, you are being tracked by a shipper.", preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
        present(alert, animated: true, completion: nil)
    }
}

My custom view:

class LoadStatus: UIView {

    @IBOutlet var contentView: UIView!

    private func commonInit(){
        Bundle.main.loadNibNamed("LoadStatus", owner: self, options: nil)
        addSubview(contentView)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder ) {
        super.init(coder: aDecoder)
        commonInit()
    }

}

Upvotes: 0

Views: 1259

Answers (2)

coyer
coyer

Reputation: 4367

As of iOS11 you need to give your customview a width and height constraint, like:

loadStatus.widthAnchor.constraint(equalToConstant: 44).isActive = true
loadStatus.heightAnchor.constraint(equalToConstant: 44).isActive = true

Otherwise your customview may be visible but internally zerosized. It's a bug.

(I assume UINavigationController is now (iOS11+) constraint-based but not before!?)

Upvotes: 2

Ahmad F
Ahmad F

Reputation: 31655

Adding a tap gesture recognizer for a UIBarButtonItem seems to be kind of weird, by default, when creating a new UIBarButtonItem -programatically as shown in your code snippet- by using init(barButtonSystemItem:target:action:), you would be able to add the desired action for the bar button, example:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // like this:
        let loadStatusButton = UIBarButtonItem(title: "Button Title", style: .plain, target: self, action: #selector(loadStatusPressed))


        // ...
    }

    @objc func loadStatusPressed() {
        print("tapped")
        let alert = UIAlertController(title: "Load Status", message: "When you are under a load, you are being tracked by a shipper.", preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
        present(alert, animated: true, completion: nil)
    }
}

And for the purpose of setting the custom view, you could simply add:

loadStatusButton.customView = loadStatus

Thus there is no need to add a tap gesture for a bar button.

Upvotes: 1

Related Questions