Skip to content
Prepare to Grow

UIKit Development

Navigation Controllers and Tab Bar Controllers

Build hierarchical and top-level UIKit navigation with system container controllers and explicit routing decisions.

Intermediate5 min read2 guided activities

Before you begin

What you will understand

  • Use navigation stacks for hierarchical flows.
  • Use tabs for peer destinations.
  • Configure navigation items and toolbars.
  • Separate route decisions from screen presentation.
01

Learning stage

Concept Explanation

UINavigationController manages an ordered stack: pushing moves deeper into a hierarchy and popping returns. Each content controller configures its own navigationItem while the container owns the navigation bar and transitions. Avoid manually adding a navigation bar to every screen.

UITabBarController switches among a small number of peer destinations. Each tab often owns a separate navigation controller so it preserves its own stack. Tabs represent stable top-level areas, not transient filters or a long list of unrelated actions.

Navigation is application state. A coordinator or router can construct controllers, translate deep links into valid stacks, and prevent individual screens from knowing every destination. Keep transitions on the main actor, avoid pushing the same route twice, and define what happens when a tab is reselected or a modal interrupts a flow.

  • Navigation stack: ordered hierarchy of pushed controllers.
  • Navigation item: title and actions contributed by content.
  • Tab: persistent peer destination.
  • Coordinator: object that constructs screens and owns route decisions.
  • Deep link: external route translated into valid application state.
02

Learning stage

Syntax & Examples

A small coordinator owns construction and push decisions while the controller emits intent.

LearningCoordinator.swiftswift
@MainActor
final class LearningCoordinator {
  let navigationController = UINavigationController()

  func start() {
    let list = CourseListViewController()
    list.onSelectCourse = { [weak self] courseID in
      self?.showCourse(id: courseID)
    }
    navigationController.setViewControllers([list], animated: false)
  }

  private func showCourse(id: Course.ID) {
    guard !(navigationController.topViewController is CourseDetailViewController) else {
      return
    }
    let detail = CourseDetailViewController(courseID: id)
    navigationController.pushViewController(detail, animated: true)
  }
}
The list reports selection; the coordinator decides which controller to build and how to present it.
03

Learning stage

Real-World Usage

Connect the UIKit concept to complete, maintainable application features.

  • Build list-to-detail-to-editor hierarchies.
  • Give Library, Search, and Profile independent tab stacks.
  • Translate a notification or universal link into a destination.
  • Centralize dependency injection during screen construction.
04

Learning stage

Common Mistakes

Identify lifecycle, ownership, identity, layout, accessibility, and user-experience risks.

  • Using tabs for steps in a linear workflow.
  • Pushing from a controller that is not in the visible navigation stack.
  • Making every screen know how to build all later screens.
  • Replacing a stack without considering back behavior and restoration.
  • Presenting modally when the destination is naturally hierarchical.
05

Learning stage

Practice Exercises

Build the behavior in a small Xcode project and inspect it across realistic states.

Activity 1

Build a two-tab shell

Create Library and Profile tabs, each containing its own navigation controller and root screen.

Hint: Give every tab a title and system image and verify each stack is preserved.

Activity 2

Route a deep link

Model a link to a course detail and build the required tab selection and navigation stack.

Hint: Validate identifiers before changing visible navigation.

06

Learning stage

Knowledge Check

Answer each question before revealing the explanation.

QUESTION 1

What relationship best fits a navigation-controller push?

  1. A.Moving deeper into a content hierarchy
  2. B.Switching between peer app areas
  3. C.Showing a brief confirmation
  4. D.Changing a button color
Reveal answer

Moving deeper into a content hierarchy

The stack preserves the path so the user can navigate back.

QUESTION 2

Why might each tab contain its own navigation controller?

Reveal answer

So each top-level area can maintain an independent hierarchical stack.

Switching tabs should not normally destroy the user's place in another area.

07

Learning stage

Interview Preparation

Explain the design in terms of lifecycle, identity, ownership, system behavior, and trade-offs.

How do you structure navigation in a UIKit app?

Short answer: Use system containers for hierarchy and peer destinations, and keep route construction in a focused coordinator when flows grow.

Explore the detailed answer

Content controllers publish user intentions. A coordinator validates routes, constructs dependencies, and pushes, presents, or selects tabs. Each tab can own a navigation stack, and deep links are translated into explicit container state rather than simulated taps.

Possible follow-ups

  • • When is a modal better than a push?
  • • How would you prevent duplicate routes?
08

Learning stage

Key Takeaways

UIKit container controllers encode familiar navigation behavior. Treat their stacks and selections as explicit state and centralize complex routing decisions.

  • Push and pop represent hierarchy.
  • Tabs represent persistent peers.
  • Content contributes navigation items; containers render bars.
  • Separate user intent from route construction.
  • Model deep links by building valid container state.