Swift
iOS Development
Navigation Controller
ViewController
SwiftUI

presenting ViewController with NavigationViewController swift

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

If you want a presented screen to have a navigation bar, you usually do not present the content view controller by itself. You wrap it inside a UINavigationController and present that navigation controller modally. That gives the presented flow its own navigation stack and bar buttons.

Presenting a View Controller Inside a Navigation Controller

Suppose DetailsViewController is the screen you want to show. Instead of presenting it directly, make it the root of a UINavigationController.

swift
1import UIKit
2
3final class HomeViewController: UIViewController {
4    @IBAction func showDetails(_ sender: Any) {
5        let detailsVC = DetailsViewController()
6        detailsVC.title = "Details"
7
8        let navController = UINavigationController(rootViewController: detailsVC)
9        navController.modalPresentationStyle = .fullScreen
10
11        present(navController, animated: true)
12    }
13}

This is the standard UIKit solution when the modal flow should still support a navigation bar, push navigation, or a close button.

Inside the presented controller you can configure navigation items normally:

swift
1import UIKit
2
3final class DetailsViewController: UIViewController {
4    override func viewDidLoad() {
5        super.viewDidLoad()
6        view.backgroundColor = .systemBackground
7        title = "Details"
8
9        navigationItem.leftBarButtonItem = UIBarButtonItem(
10            barButtonSystemItem: .close,
11            target: self,
12            action: #selector(closeTapped)
13        )
14    }
15
16    @objc private func closeTapped() {
17        dismiss(animated: true)
18    }
19}

Because the controller sits inside a UINavigationController, the bar button appears in the navigation bar automatically.

When to push Instead of present

If the current screen is already inside a navigation controller and the new screen belongs in the same hierarchy, use pushViewController instead of present.

swift
let detailsVC = DetailsViewController()
navigationController?.pushViewController(detailsVC, animated: true)

Use push when the user is moving deeper into the current flow.

Use present when the new screen is a separate modal flow, such as onboarding, settings, login, or a create-item wizard.

Storyboard Version

If you use storyboards, the principle is the same. Instantiate the target controller, wrap it, then present the wrapper.

swift
1let storyboard = UIStoryboard(name: "Main", bundle: nil)
2let detailsVC = storyboard.instantiateViewController(withIdentifier: "DetailsViewController")
3let navController = UINavigationController(rootViewController: detailsVC)
4present(navController, animated: true)

The navigation controller does not have to exist in the storyboard already. Creating it in code is often simpler.

Customizing the Presented Navigation Flow

Since the presented controller has its own navigation stack, it can push additional screens.

swift
1final class DetailsViewController: UIViewController {
2    @objc func showMore() {
3        let moreVC = MoreInfoViewController()
4        navigationController?.pushViewController(moreVC, animated: true)
5    }
6}

This is one of the main reasons to present a navigation controller rather than a plain view controller.

You can also style the navigation bar independently for the modal flow.

swift
navController.navigationBar.prefersLargeTitles = true

That keeps modal navigation concerns separate from the main app stack.

Common Pitfalls

A common mistake is presenting DetailsViewController directly and then wondering why navigationItem buttons do not appear. There is no navigation bar unless the controller is inside a navigation controller.

Another issue is using present for screens that should really be pushed onto the existing stack. That can create awkward UX with nested modal flows.

Developers also sometimes dismiss the wrong controller. If the content is embedded in a presented navigation controller, calling dismiss(animated:) from the root content controller is usually fine, but it is dismissing the whole modal navigation flow.

Finally, do not confuse UINavigationController with SwiftUI NavigationView. The title here is about UIKit presentation, not SwiftUI navigation containers.

Summary

  • To show a modal screen with a navigation bar, wrap the content controller in UINavigationController.
  • Present the navigation controller, not the content controller alone.
  • Use pushViewController instead when the screen belongs in the current navigation stack.
  • Configure bar buttons on the embedded content controller through navigationItem.
  • Keep modal flows and main navigation flows conceptually separate.

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.