How to dismiss ViewController in Swift?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
How you dismiss a view controller in Swift depends on how it was shown. If it was presented modally, call dismiss(animated:completion:). If it was pushed onto a navigation stack, you usually pop it with the navigation controller instead of dismissing it.
That distinction matters because dismiss and popViewController solve different presentation models. Many bugs come from calling the right method on the wrong kind of presentation.
Dismiss a Modally Presented Controller
If the current controller was shown with present, dismiss it like this:
This is the standard answer for modals such as sheets, full-screen dialogs, or presented forms.
If one controller presented another which then presented another, calling dismiss on the currently visible one dismisses that presented chain back to the presenting controller.
Pop a Controller from a Navigation Stack
If the controller was pushed with navigationController?.pushViewController, it is not a modal. In that case, pop it:
That removes only the top controller from the navigation stack.
To go all the way back to the root controller:
So the practical rule is simple:
- use
dismissfor modal presentation - use
popViewControllerfor navigation-stack presentation
Dismiss from the Presenting Controller
Sometimes you are inside the presenting controller and want to dismiss what you showed earlier. That also works:
This is less common for button actions inside the presented screen itself, but it can be useful in coordinator-style flows.
Detect the Situation if Needed
If a controller might appear in different ways, you can inspect its context before deciding what to do. In many apps this is unnecessary because the flow is known in advance, but the idea is straightforward:
- if the controller has a presenting controller, it may be dismissible as a modal
- if it is inside a navigation stack, it may need popping instead
In practice, it is usually better to design the screen so the dismissal path is explicit rather than guessing at runtime. Clear flow ownership also makes custom transitions and coordinator logic easier to maintain. It also makes testing navigation behavior more predictable. It reduces confusion in larger apps too.
Avoid Mixing Presentation Styles Casually
A controller embedded in a navigation controller may itself be presented modally. In that case, the right action depends on the outer presentation model.
For example, if you present a navigation controller modally and then push several screens inside it, dismissing the modal flow means dismissing the navigation controller, not popping only the top child screen.
That is why understanding the container hierarchy matters in non-trivial flows.
Common Pitfalls
- Calling
dismisson a controller that was pushed onto a navigation stack. - Calling
popViewControllerwhen the screen was presented modally. - Forgetting that a modally presented navigation controller wraps multiple pushed screens.
- Guessing the presentation style instead of understanding how the screen was shown.
Summary
- Use
dismiss(animated:completion:)for modally presented view controllers. - Use
navigationController?.popViewController(animated:)for pushed controllers. - Use
popToRootViewControllerwhen you need to return to the root of a navigation stack. - Be careful with flows where a navigation controller itself is presented modally.
- The correct dismissal method depends on the original presentation style, not just on the current screen.

