How to Fix iOS Navigation Controller Push Error
In this tutorial, you'll learn about How to Fix iOS Navigation Controller Push Error. We cover key concepts, practical examples, and best practices.
The Problem
You call pushViewController and get:
Attempt to push a view controller that is already on the navigation stack
Or:
Finishing up a navigation transition in an unexpected state
Or the back button disappears after pushing a view controller.
Quick Fix
Step 1: Check if a navigation controller exists
// Ensure the current view controller is inside a navigation stack
guard let navController = self.navigationController else {
print("No navigation controller")
return
}
navController.pushViewController(detailVC, animated: true)
If navigationController is nil, the view controller was not embedded in a UINavigationController.
Step 2: Prevent double-push on the same instance
// Wrong - pushing the same instance twice
let vc = DetailViewController()
navigationController?.pushViewController(vc, animated: true)
navigationController?.pushViewController(vc, animated: true) // crash
// Right - create a new instance each time
navigationController?.pushViewController(DetailViewController(), animated: true)
Step 3: Use present instead of push for modals
// For standalone flows that are not part of the navigation hierarchy
present(detailVC, animated: true, completion: nil)
Use dismiss(animated:) to return from a presented view controller.
Step 4: Avoid pushing during transitions
// Delay the push until the current transition ends
navigationController?.pushViewController(detailVC, animated: false)
If you need to push immediately after another navigation action, use a short delay:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.navigationController?.pushViewController(detailVC, animated: true)
}
Step 5: Check the storyboard segue
If using segues, ensure the segue type is Show (e.g. Push) and not Present Modally:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let destVC = segue.destination as! DetailViewController
destVC.item = selectedItem
}
}
Step 6: Use setViewControllers for stack management
// Replace the entire navigation stack
navigationController?.setViewControllers([rootVC, detailVC], animated: true)
This is useful for login flows where the back button should not return to the login screen.
Step 7: Handle the case where navigation bar is hidden
navigationController?.setNavigationBarHidden(false, animated: true)
A hidden navigation bar prevents the back button from appearing.
Prevention
- Always check
navigationControlleris not nil before pushing. - Create new view controller instances per push navigation event.
- Avoid pushing while the navigation controller is still animating.
Common Mistakes with navigation controller
- Misunderstanding that
Stringis[Char]with poor performance for large text operations - Using
foldlinstead offoldl'causing stack overflow on large lists - Forgetting
deriving (Show, Eq)on custom data types needed for debugging
These mistakes appear frequently in real-world IOS code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
DodaTech Tool Reference
Doda Browser's Navigation Flow tool visualizes the current navigation stack of any running iOS app, helping developers debug push/pop state issues.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro