iOS development
UINavigationController
Swift programming
rootViewController
app development

Set rootViewController of UINavigationController by method other than initWithRootViewController

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

A UINavigationController does not expose a writable rootViewController property the way people often expect. Its "root" is simply the first view controller in the navigation stack. So if you want to change it after initialization, the usual solution is to replace the navigation controller's viewControllers array instead of calling init(rootViewController:) again.

Replace the stack with setViewControllers

The standard runtime approach is:

swift
1import UIKit
2
3let home = HomeViewController()
4let navigationController = UINavigationController()
5navigationController.setViewControllers([home], animated: false)

After that call, home is the root of the navigation stack because it is the first and only controller in the array.

This is the cleanest answer when the navigation controller already exists and you want to reset or replace its stack.

You can also assign viewControllers directly

A simpler equivalent is direct assignment:

swift
let login = LoginViewController()
navigationController.viewControllers = [login]

This works too, but setViewControllers(_:animated:) is often clearer when you want to express a stack replacement operation explicitly.

Know the difference between window root and navigation root

There are two related but different questions developers often mix together:

  • changing the root controller inside a navigation controller
  • changing the app window's root view controller

Changing the navigation root means replacing navigationController.viewControllers.

Changing the app's root screen means assigning the window's root controller:

swift
window?.rootViewController = navigationController
window?.makeKeyAndVisible()

These are not interchangeable. One replaces the stack inside a navigation controller. The other replaces the entire app entry point.

Resetting the stack is useful after login or onboarding

A common pattern is to remove old screens from the back stack after a flow completes. For example, after login succeeds:

swift
let main = MainViewController()
navigationController.setViewControllers([main], animated: true)

That makes MainViewController the new root of that navigation flow, so the user cannot navigate back to the login screen with the normal back button.

This is usually better than pushing the next screen on top of the old flow when the old screens should no longer be reachable.

Storyboards and container setup still matter

If the navigation controller is created in a storyboard, the same stack-replacement idea still applies. The main difference is how you obtain the target view controller instance.

For example:

swift
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let dashboard = storyboard.instantiateViewController(withIdentifier: "DashboardViewController")
navigationController?.setViewControllers([dashboard], animated: false)

What matters is not how the navigation controller was created. What matters is that the first element of its stack defines the root.

Common Pitfalls

The most common mistake is looking for a writable rootViewController property on UINavigationController. The root is represented by the first controller in the stack.

Another common issue is confusing window.rootViewController with the navigation controller's internal root screen. They affect different levels of the UI hierarchy.

People also push a new controller when they actually wanted to replace the entire stack, which leaves unwanted screens reachable through the back button.

Finally, if you replace the stack during a flow transition, make sure you are doing it on the correct navigation controller instance. In complex apps there may be more than one.

Summary

  • The root of a UINavigationController is the first controller in its viewControllers stack.
  • After initialization, replace the root by setting a new stack with setViewControllers or viewControllers.
  • Do not confuse the navigation root with window.rootViewController.
  • Replacing the stack is a common way to reset navigation after login or onboarding.
  • Think in terms of stack replacement, not a writable root property.

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.