iOS development
Swift programming
presentViewController
navigation bar
UIKit

presentViewController and displaying navigation bar

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

Calling present on a view controller does not automatically give the presented screen a navigation bar. A navigation bar appears only when the presented controller is inside a UINavigationController, or when you push onto an existing navigation stack instead of presenting modally.

present and push Solve Different Problems

In UIKit, modal presentation and navigation-stack pushing are separate concepts.

  • 'present(_:animated:) shows a new controller modally.'
  • 'navigationController?.pushViewController(_:animated:) pushes onto an existing navigation stack.'

If you present a plain UIViewController, there is no navigation controller wrapped around it by default, so there is no navigation bar to display.

The Common Fix: Present a Navigation Controller

If you want a modally presented screen that still has a navigation bar, embed the destination controller in a UINavigationController first.

swift
1import UIKit
2
3final class HomeViewController: UIViewController {
4    @IBAction func showDetails(_ sender: UIButton) {
5        let details = DetailsViewController()
6        details.title = "Details"
7
8        let navController = UINavigationController(rootViewController: details)
9        present(navController, animated: true)
10    }
11}
12
13final class DetailsViewController: UIViewController {
14    override func viewDidLoad() {
15        super.viewDidLoad()
16        view.backgroundColor = .systemBackground
17
18        navigationItem.rightBarButtonItem = UIBarButtonItem(
19            barButtonSystemItem: .done,
20            target: self,
21            action: #selector(closeScreen)
22        )
23    }
24
25    @objc private func closeScreen() {
26        dismiss(animated: true)
27    }
28}

Now the modal screen gets a navigation bar because the visible controller lives inside a navigation controller.

If You Are Already in a Navigation Stack, Push Instead

Sometimes the real issue is that the screen should not be modal in the first place. If the current controller already belongs to a navigation controller and the user expects back navigation, pushing is often the better UX.

swift
1@IBAction func showDetails(_ sender: UIButton) {
2    let details = DetailsViewController()
3    details.title = "Details"
4    navigationController?.pushViewController(details, animated: true)
5}

This automatically shows the navigation bar from the existing stack, along with the standard back button behavior.

Storyboard Version

If you use storyboards, the same principle applies. Either:

  • embed the destination scene in a navigation controller before presenting it,
  • or segue into the current navigation stack with a push segue instead of a modal segue.

The problem is not storyboard-specific. It is about whether a navigation controller exists in the presentation chain.

Configuring the Navigation Bar

Once the presented controller is embedded properly, configure it through navigationItem.

swift
1override func viewDidLoad() {
2    super.viewDidLoad()
3    title = "Edit Profile"
4    navigationItem.largeTitleDisplayMode = .never
5}

You can also customize bar buttons, title behavior, and appearance the same way you would for any pushed controller.

Full-Screen and Sheet Presentations

Modern iOS often presents controllers as sheets. That does not change the core rule: sheet or full-screen, a navigation bar still requires a navigation controller if you want top-bar navigation controls.

swift
1let editor = EditorViewController()
2let nav = UINavigationController(rootViewController: editor)
3nav.modalPresentationStyle = .formSheet
4present(nav, animated: true)

This gives you a sheet presentation with a proper navigation bar.

Why navigationController Is Sometimes nil

Developers often try to access self.navigationController in a presented controller and find it is nil. That happens when the controller was presented directly and not embedded in a navigation controller.

Once you present UINavigationController(rootViewController: someVC), the child controller's navigationController property is no longer nil.

Common Pitfalls

The biggest pitfall is assuming modal presentation automatically includes navigation UI. It does not.

Another mistake is using present when the user flow really wants hierarchical navigation. In that case, pushViewController is more appropriate and simpler.

Developers also sometimes embed the wrong controller. The navigation controller must wrap the destination controller before presentation, not after it is already on screen.

Finally, if the navigation bar still does not appear, check whether you accidentally hid it elsewhere with setNavigationBarHidden.

Summary

  • 'present does not create a navigation bar on its own.'
  • To show a navigation bar modally, present a UINavigationController whose root is your destination controller.
  • If the screen belongs in the current flow, use pushViewController instead of present.
  • Configure titles and buttons through navigationItem after embedding correctly.
  • If navigationController is nil, the controller was probably presented without a navigation wrapper.

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.