UIBarButtonItem
target-action
iOS development
Swift
troubleshooting

UIBarButtonItem target-action not working?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

When a UIBarButtonItem tap does nothing, the issue is almost always one of these: the target is nil (deallocated or never set), the action selector is misspelled or has the wrong signature, the button was created with a custom view that absorbs touches, or isEnabled is false. Unlike UIButton, UIBarButtonItem does not inherit from UIView and has its own quirks with the target-action pattern.

Basic Correct Setup

swift
1class ViewController: UIViewController {
2    override func viewDidLoad() {
3        super.viewDidLoad()
4
5        navigationItem.rightBarButtonItem = UIBarButtonItem(
6            title: "Save",
7            style: .plain,
8            target: self,
9            action: #selector(saveTapped)
10        )
11    }
12
13    @objc func saveTapped() {
14        print("Save tapped!")
15    }
16}

Key requirements:

  • target is self (not nil)
  • Action method is marked @objc
  • Selector matches the method name exactly

Problem 1: Target is nil

swift
1// WRONG — target is nil, action goes nowhere
2navigationItem.rightBarButtonItem = UIBarButtonItem(
3    title: "Save",
4    style: .plain,
5    target: nil,           // <-- nil target
6    action: #selector(saveTapped)
7)

When target is nil, UIKit sends the action up the responder chain. If no responder implements the selector, nothing happens — no crash, no warning, just silence.

Fix: Always set target: self (or the appropriate object).

Problem 2: Missing @objc

swift
1// WRONG — method not exposed to Objective-C runtime
2func saveTapped() {
3    print("Save tapped!")
4}
5
6// CORRECT
7@objc func saveTapped() {
8    print("Save tapped!")
9}

The target-action pattern uses Objective-C message dispatch. Without @objc, the method is invisible to the runtime. In modern Swift, the compiler usually catches this with #selector, but it can slip through if you set the action as a string.

Problem 3: Wrong Selector Signature

swift
1// These are DIFFERENT selectors:
2#selector(saveTapped)          // saveTapped()
3#selector(saveTapped(_:))      // saveTapped(_ sender: UIBarButtonItem)
4
5// If your method has a parameter:
6@objc func saveTapped(_ sender: UIBarButtonItem) {
7    print("Tapped: \(sender.title ?? "")")
8}
9
10// Use the matching selector:
11UIBarButtonItem(
12    title: "Save",
13    style: .plain,
14    target: self,
15    action: #selector(saveTapped(_:))  // Note the (_:)
16)

A mismatch between the selector and method signature causes a crash (unrecognized selector) or silent failure.

Problem 4: Custom View Absorbs Touches

swift
1// WRONG — custom view button may intercept touches
2let button = UIButton(type: .system)
3button.setTitle("Save", for: .normal)
4
5let barButton = UIBarButtonItem(customView: button)
6barButton.target = self
7barButton.action = #selector(saveTapped)
8// target/action on UIBarButtonItem is IGNORED for custom views

When you create a UIBarButtonItem with customView:, the bar button item's own target and action are not used. The custom view handles its own touches.

Fix: Add the action to the custom view directly:

swift
1let button = UIButton(type: .system)
2button.setTitle("Save", for: .normal)
3button.addTarget(self, action: #selector(saveTapped), for: .touchUpInside)
4
5navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)

Problem 5: Button is Disabled

swift
1let barButton = UIBarButtonItem(
2    title: "Save",
3    style: .plain,
4    target: self,
5    action: #selector(saveTapped)
6)
7barButton.isEnabled = false  // Grayed out, won't respond to taps
8
9navigationItem.rightBarButtonItem = barButton

Check that isEnabled is true. The button appears dimmed when disabled, but it can be hard to notice.

Problem 6: View Controller is Deallocated

swift
1func setupNavigation() {
2    let vc = DetailViewController()
3    // vc is a local variable — it gets deallocated after this method returns
4    navigationItem.rightBarButtonItem = UIBarButtonItem(
5        title: "Save",
6        style: .plain,
7        target: vc,         // vc is about to be deallocated
8        action: #selector(DetailViewController.saveTapped)
9    )
10}

The target is not retained by UIBarButtonItem. If the target object is deallocated, the action goes to nil (no crash, no response).

Fix: Ensure the target stays alive for the lifetime of the button.

Problem 7: System Item with Target-Action

swift
1let doneButton = UIBarButtonItem(
2    barButtonSystemItem: .done,
3    target: self,
4    action: #selector(doneTapped)
5)
6// This works correctly — system items support target-action
7toolbarItems = [doneButton]

System items (.done, .cancel, .add, etc.) work with target-action. But make sure the toolbar or navigation bar is actually visible.

Problem 8: Not in Navigation Controller

swift
1// This button won't appear if there's no navigation controller
2navigationItem.rightBarButtonItem = UIBarButtonItem(
3    title: "Save",
4    style: .plain,
5    target: self,
6    action: #selector(saveTapped)
7)
8
9// Check: is the view controller inside a UINavigationController?
10print(navigationController)  // nil means no navigation bar is shown

navigationItem only shows buttons when the view controller is embedded in a UINavigationController.

Debugging Checklist

swift
1// 1. Is the button visible?
2print(navigationItem.rightBarButtonItem)  // Should not be nil
3
4// 2. Is target set?
5print(navigationItem.rightBarButtonItem?.target)  // Should not be nil
6
7// 3. Is it enabled?
8print(navigationItem.rightBarButtonItem?.isEnabled)  // Should be true
9
10// 4. Is there a navigation controller?
11print(navigationController)  // Should not be nil
12
13// 5. Does the action method exist?
14print(responds(to: #selector(saveTapped)))  // Should be true

Common Pitfalls

  • Custom view ignores target-action: UIBarButtonItem(customView:) does not use its own target/action. Add touch handling to the custom view itself.
  • Weak reference to target: UIBarButtonItem does not retain its target. If the target is deallocated (common with closures or temporary objects), taps silently fail.
  • Setting action after init: Assigning barButton.action = #selector(newAction) works, but make sure barButton.target is also set — it defaults to nil.
  • Toolbar vs Navigation bar: toolbarItems appear in the toolbar (at the bottom), navigationItem buttons appear in the navigation bar (at the top). Putting buttons in the wrong collection makes them invisible.
  • Responder chain confusion: With target: nil, the action travels the responder chain. This works only if a responder in the chain implements the selector. It is fragile and hard to debug — always set an explicit target.

Summary

  • Always set target: self and mark action methods with @objc
  • Ensure the selector matches the method signature exactly (saveTapped vs saveTapped(_:))
  • Custom view bar button items ignore target/action — add actions to the custom view directly
  • Check isEnabled, verify the target is not deallocated, and confirm a UINavigationController exists
  • Use responds(to:) and print debugging to verify the target-action chain

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.