Swift Swift: Using The Navigation Bar Title and Back button.

[Updated for Xcode 7.2/Swift 2.1 2/5/16]

Note: Click here for the  Swift 3.0/Xcode 8 version of this tutorial 

In writing the Swift Swift View Controllers book, it came to my attention many people don’t understand the functionality of the navigation toolbar’s title and Back button. In an early part of writing, I planned to skip the topic as a minor detail  so I could get the book done and published. However, the built-in features of the navigation toolbar make it a powerful and useful feature that can’t be missed.

Setting Up the Storyboard

Open up a new single view project named NavBarDemo using Swift as the language and Universal device. Go to the storyboard and click the view controller icon in the scene. In the drop down menu, select Editor>Embed in>Navigation controller. Drag two buttons, one labeled Pizza and the other labeled Pasta out to the scene. Make the Pizza button white text on a red background. Make the Pasta button white text on a blue background. Arrange them like this on the storyboard:

Screenshot 2015-03-21 09.22.36

Select the Pizza button. Using auto layout, pin the button 0 points up, 0 left, 0 right, and 0 down like this:

Screenshot 2015-03-21 09.27.51

Select the Pasta button and again pin the button button 0 up,0 left, 0 right, and 0 down. Now Control-drag from the Pizza button to the Pasta button. Select Equal Widths in the menu that appears.

Screenshot 2015-03-21 09.29.26

On the resolver menu select Update Frame in the All Frames in View section. Your controller should look like this:

Screenshot 2015-03-21 09.32.34

Drag two more view controllers on to the storyboard. Make the background of one blue and the background of the other red.
Control drag from the Pizza button to the red scene. Select a show segue. In the attributes inspector, set the segue identifier to pizza. Control-drag from the Pasta button to the blue scene. Select a show segue. In the properties inspector, set the segue identifier to pasta.

In this lesson we will do all the coding in ViewController, so there is no need of code in the two new controllers.

Setting the Navigation Title Bar

There is a property on UIViewController called navigationItem. When used with navigation controllers, this controls the navigation bar at the top of the view. The navigationItem property is an instance of UINavigationItem, which contains four major properties: a title, a left bar button, a right bar button and a prompt. To set the title on the toolbar , you set the string for the title property. For example add this to the ViewController class

override func viewWillAppear(animated: Bool) {
        navigationItem.title = "One"
    }

Build and run . The first view now has One as a title.

Screenshot 2015-03-21 09.43.19

We used viewWillAppear and not viewDidLoad. ViewController is the root view controller in the navigation stack. It only loads once and stays active in the background. It will execute viewDidload only once. To make sure we update, we use viewWillAppear instead.
The title can be dynamic. As a simple example, We’ll place a count in the title of the navigation bar. Add the following code:

var vcCount:Int = 0{
    didSet{
      navigationItem.title = "Count: \(vcCount)"
    }
  }

We used the  didSet property observer feature of Swift. Any time vcCount changes, the title changes with it. We change the count on any segue so we can increment in prepareForSegue. Add this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
    }

It’s rare to not have a statement within an if clause in a prepareForSegue. We want any segue to increment the count, so we don’t need the if.

We want to show the count when we load. Change the viewWillAppear to this:

 override func viewWillAppear(animated: Bool) {
        // navigationItem.title = "One"
        navigationItem.title = "Count: \(vcCount)"
    }

Build and run. Go back and forth in the views. The title changes every time you come back to it.

Screenshot 2015-03-21 10.55.57 Screenshot 2015-03-21 10.56.01

Programming the Back Button

You’ll notice once you have a title, the navigation Back button disappears, to be replaced by the previous view’s title.

The Back button reads the controller underneath the current controller for its title. You cannot set the back button title directly. Instead, you set the title of the current controller before you leave for the destination controller. If the previous view controller’s title is nil, the button titles itself Back. Change the prepareForSegue to this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil
    }

Build and run. You have a Back button with the count:

Screenshot 2015-03-21 10.53.15  Screenshot 2015-03-21 10.53.27

If you wanted to add your own text to the Back button, you have to change the title of the controller below it on the navigation stack. Change prepareForSegue to this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil

        if segue.identifier == "pizza"{
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            navigationItem.title = "Pasta to One"
        }
    }

Before we segue to the pizza and pasta controller, we change the title of the current controller’s navigationItem. The Back button reads the title of the and sets the button’s title accordingly. The viewWillAppear method will reset the title to the count in the first view controller when we pop the Pizza or Pasta view controller. Build and Run. Select to the Pizza and Pasta buttons:

Screenshot 2015-03-21 11.00.26 Screenshot 2015-03-21 11.00.32

To see this happening, you can comment out this in viewWillAppear.

//navigationItem.title = "Count: \(vcCount)"

Build and Run. When you go back, the title remains:

Screenshot 2015-03-21 11.02.02 Screenshot 2015-03-21 11.02.08

Uncomment the line before you go on.

The Size-sensitive Back Button.

The Back button is sensitive to the space around it. The button’s title responds to not having enough space to place itself. Change the prepareForSegue above to this:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil
        if segue.identifier == "pizza"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Pizza"
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Linguine all’arrabbiata"
            navigationItem.title = "Pasta to One"
        }
    }

Lines 5 and 10 get the destination view controller and assign it to vc. We dynamically send the title by vc.navigationItem.title in lines 6 and 11. We’ve picked some long labels to test size restrictions. Set the simulator to iPad 2. Build and run. Select the Pizza button.

 Screenshot 2015-03-21 11.09.25

Go back, change the switch and select the pasta button.

Screenshot 2015-03-21 11.09.19

In both cases, we get the title we wanted in the back button. Stop the simulator and change to an iPhone 6. Run again, change the switch for Pasta and you get something different.

Screenshot 2015-03-21 15.25.37

We lose our custom title and we have a back button again. Now try an iPhone 4s in the simulator. Run the demo, switch to the Pasta controller and you get this:

Screenshot 2015-03-21 15.26.52

There is only the <  icon and no text for the back button. Rotate the device by pressing Command-Left Arrow:

Screenshot 2015-03-21 15.26.57

The text re-appears with more space to place the text.

The back button is intelligent. If it has enough space, it displays the title of the previous controller. If there is not enough space, it displays Back. If there is not enough space for the word Back, it displays only the  < icon.
There is an important design rule in play here that I am intentionally breaking to make the point: Keep your titles short in a navigation bar. If you keep titles short, you will have the full functionality of the back button.

The Whole Code

Downloading Code

I’m trying an new way of sharing sourcecode. The will be a .zip file for download at the the end of the lesson. It will contain sourcecode and assets only.  You will have to build your own application.

Here is the source code for the lesson: NavBarDemo_Source.

Download it and unzip it. You will find two files. ViewController.Swift and Main.Storyboard. In Xcode, Open up a new single view project named NavBarDemo using Swift as the language and Universal device. Delete the ViewController.Swift and Main.Storyboard and send them to the trash can. Drag the files you unzipped to the project’s navigator. In the dialog box that appears, make sure  that copy files is checked on, and finish the dialog box.  You now have a working copy.

ViewController.swift

//
//  ViewController.swift
//  NavBarDemo
//
//  Created by Steven Lipton on 3/21/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    var vcCount:Int = 0{
        didSet{
            navigationItem.title = "Count: \(vcCount)"
        }
    }

    override func viewWillAppear(animated: Bool) {
        // navigationItem.title = "One"
        navigationItem.title = "Count: \(vcCount)"
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil

        if segue.identifier == "pizza"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Pizza"
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Linguine all’arrabbiata"
            navigationItem.title = "Pasta to One"
        }
    }
}

16 Replies to “Swift Swift: Using The Navigation Bar Title and Back button.”

  1. Hey! Just wanna thank you for all these tutorials, I really enjoyed them! However, is there a way to change the height of the navigation bar in Swift? I have found solutions in Objective C and tried converting them into Swift but to no avail. I would really appreciated it if there was any guide in Swift! Many thanks!!

  2. very useful, thanks.Just one thing: it’s “linguinE”, female and plural – not linguini – and linguine can be “all’arrabbiata” -. I think it’s important for iOS developers to know

  3. Hi Steve, I’m stuck on something and hope you can help. :)

    Situation:In Swift 2.0, I have a Navigation Bar with a Button Bar Item on the right and a Tool Bar with a Button Bar Item on the right. When the Navigation Bar’s Button Bar Item is tapped I want it disabled and then reenabled when the Tool Bar’s Button Bar Item is tapped. I tried the following and it didn’t work. :(

    Navigation Button Bar Item:
    @IBAction func play(sender: AnyObject)
    {
    self.navigationItem.rightBarButtonItem?.enabled = false
    ...
    }

    Tool Bar Button Bar Item:
    @IBAction func pause(sender: AnyObject)
    {
    self.navigationItem.rightBarButtonItem?.enabled = true
    ...
    }

    Thanks,
    Stef

    1. The code above worked on my system just fine. Make sure you hooked up everything correctly. However, is not how I would do this code. I like to keep things simple, so I would just make an outlet for the navigation bar button like this:

      class ViewController: UIViewController {
      
          
          @IBOutlet weak var play: UIBarButtonItem!
          @IBAction func play(sender: UIBarButtonItem!)
          {
              //self.navigationItem.rightBarButtonItem?.enabled = false
              play.enabled = false
          }
             @IBAction func pause(sender: UIBarButtonItem!)
          {
              //self.navigationItem.rightBarButtonItem?.enabled = true
             play.enabled = true
          }
      }

      It is a lot simpler and better documentation, including the use of UIBarButtonItem! as the type of sender. I strongly suggest not using AnyObject in sender.

      I don’t know your context, but it seems strange to have two buttons with related functions so far away from each other. That’s generally not good user interface design. Particularly since you can have only one function at time, which is why you are disabling the button anyway, just use one button that changes title or image. I’d use a toggle like this:

      class ViewController: UIViewController {
          //toggle solution
          func playStuff(){
             //do play stuff here
          }
          func pauseStuff(){
              //do pause stuff here
          }
          @IBAction func play(sender: UIBarButtonItem!){
              if sender.title == "Play"{  //play event
                  play.title = "Pause"
                  playstuff()
              }else{  //pause event
                  play.title = "Play"
                  pauseStuff()
              }
      
          }
      }
      

      The less buttons the better.

      1. Thanks, Steve. I knew I could count on your help! When I Googled my issue your site came up near the top of list. I thought to myself how silly I was to not look here first! ;)

        I was following a tutorial and the “demo” app was introducing Navigation Bar and Tool Bar. I agree, having them so far apart is not super friendly, but tutorial is to demonstrate using the two different bars each having different actions.

        I have a tendency to “try to break itt” when testing and I noticed that if I hit play and then play again that horrible behavior happens. So, I thought I would experiment with disabling. The app uses the Bar Button Item “System Item” of “Play” which puts the play triangle image and the other displays the pause image.

        I like your approach. I will go back and see if I didn’t follow along correctly and use the AnyObject by mistake. :o

        I’m glad I reached out to you! Thank you again! (one of the days I will have a working app). Now to figure out how to dynamically change the Bar Button Item image.

        Stef :D

      2. It’s likely that the code you used had AnyObject — they didn’t make it optional either.
        And good for you to try to break code. It helps with the learning process and a great debugging skill.
        Someone else’s app –That explains the context.

  4. Hi, there!
    Great article, thanks for that!
    I came here on search about a question that is still unanswered; I don’t know how to obtain an answer. What I want is to (programmatically, if necessary – doesn’t seem to work in IB) add a second left bar button item to the navigation bar that is controlled by the navigation controller. The navigation controller, obviously, adds the back button on the left side as the bar button item – and when I then append a second bar button item to the “leftBarButtonItems” array in my viewController’s “viewWillAppear” method, that second item does not appear on the left side, between the navigation controller’s “back” button and the title. I can programmatically add items on the right side, to the “rightBarButtonItems” array.
    So, my question is: is it at all possible to add a second bar button item in a navigation controller, when the navigation controller shows a “back” button (which I need, and want to keep)? If so, how?

    Thanks for the great work again! Best regards,
    Björn

    1. Sorry for the delay in posting to you. To my knowledge no, you can’t. I think there’s a few reasons for that. First, the title has priority over the buttons. an extra button takes valuable real estate from the title. Secondly, that’s not a great place for buttons UI. I won’t go into details, here’s an article on The thumb zone for more info, but that’s a really bad point on the thumb zone, and buttons will be hit inaccurately a lot of the time on a device someone is using singlehandedly. They are as likely to hit back as your button.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s