Fix: Back Button Text Not Changing in UINavigationController

Fix: Back Button Text Not Changing in UINavigationController

Written by

in

Quick answer: The back button belongs to the previous view controller, not the one on screen. Set navigationItem.backBarButtonItem on the view controller that does the pushing, before the push happens. Use a new UIBarButtonItem with the title you want, or an empty string to hide the text.

Why this happens

You are on screen B and want to change the text of its back button. So you write self.navigationItem.backBarButtonItem?.title = "Back" in B. Nothing changes.

The reason is ownership. The back button shown on B is configured by the view controller below it in the navigation stack, which is A, the one that pushed B. Each view controller’s navigationItem describes how that controller appears in the bar, and its backBarButtonItem describes how the next controller’s back button will look.

The other attempts fail for related reasons. navigationBar.backItem is a read-only reference to the previous UINavigationItem, and navigationController?.navigationItem is the item of the navigation controller itself, which is not shown in its own bar. Also, backBarButtonItem is nil until you assign one, so backBarButtonItem?.title = ... silently does nothing.

How to fix it

1. Set backBarButtonItem on the previous view controller

In the view controller that pushes, assign a new UIBarButtonItem. Do this in viewDidLoad or viewWillAppear, before the push:

override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.backBarButtonItem = UIBarButtonItem(
        title: "Something Else",
        style: .plain,
        target: nil,
        action: nil
    )
}

When A pushes B, B’s back button now reads < Something Else. Leave target and action as nil. The navigation controller handles the pop itself.

2. Set it in prepare(for:sender:) when using segues

If you push through a storyboard segue, set the item right before the transition happens:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let backItem = UIBarButtonItem()
    backItem.title = "Something Else"
    navigationItem.backBarButtonItem = backItem
}

Note that navigationItem here is the item of the current (source) view controller. It affects the destination’s back button. In Swift 2 the method was prepareForSegue(_:sender:) with AnyObject?. In Swift 3 and later use the signature above.

3. Use Interface Builder

You can skip code entirely:

  1. Open the storyboard and select the Navigation Item of the previous view controller (A, not B).
  2. Open the Attributes inspector.
  3. Type your text into the Back Button field.

This writes the same backBarButtonItem for you.

4. Hide the text and keep only the arrow

To remove the text, use an empty title:

navigationItem.backBarButtonItem = UIBarButtonItem(
    title: "",
    style: .plain,
    target: nil,
    action: nil
)

On newer iOS versions there is also a backButtonDisplayMode property on UINavigationItem that you can set to .minimal to show only the chevron. Set it on the pushing view controller as well:

navigationItem.backButtonDisplayMode = .minimal

5. Change the title of the current screen instead

If the back button shows the previous screen’s title and you just want a shorter one, set a short title on the previous view controller. UIKit uses it as the default back text. Setting backBarButtonItem is better when the screen title must stay long.

How to prevent it

  • Remember the rule: configure the back button on the controller that pushes, not the one that is pushed.
  • Set backBarButtonItem in viewDidLoad so it is ready before any push.
  • Do not mutate backBarButtonItem?.title. Assign a fresh UIBarButtonItem.
  • Prefer Interface Builder’s Back Button field for simple storyboard apps.
  • Use backButtonDisplayMode when you only want to hide the text.

FAQ

Why does navigationItem.backBarButtonItem?.title = "Back" do nothing?

The property is nil by default, so optional chaining skips the assignment. Even if it were set, you were editing it on the wrong view controller. Assign a new UIBarButtonItem on the previous screen.

Can I change the back button from the pushed view controller?

Not through backBarButtonItem. You can instead hide the system button with navigationItem.hidesBackButton = true and supply your own leftBarButtonItem, but then you must handle the pop yourself and you lose the swipe-back gesture unless you restore it.

Does this work with SwiftUI’s NavigationStack?

No, this is UIKit. In SwiftUI the back button text comes from the previous screen’s navigation title. If you need a custom button, hide the default with .navigationBarBackButtonHidden(true) and add your own in a toolbar.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.