iOS
view controller
dismiss detection
Swift
app development

Detect when a presented view controller is dismissed

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When one view controller presents another, the presenting screen often needs to know when the modal goes away so it can refresh data, resume work, or update its UI. In UIKit, the correct detection strategy depends on how the dismissal happens: by button tap, by interactive swipe, or by code.

Notifying the Presenter Explicitly

If the presented controller decides when it is done, the cleanest option is to notify the presenter before dismissal. A delegate works well because it makes the data flow explicit and avoids hidden behavior.

swift
1import UIKit
2
3protocol EditProfileViewControllerDelegate: AnyObject {
4    func editProfileViewControllerDidFinish(_ controller: EditProfileViewController)
5}
6
7final class ProfileViewController: UIViewController, EditProfileViewControllerDelegate {
8    @IBAction private func editButtonTapped(_ sender: UIButton) {
9        let editor = EditProfileViewController()
10        editor.delegate = self
11        editor.modalPresentationStyle = .pageSheet
12        present(editor, animated: true)
13    }
14
15    func editProfileViewControllerDidFinish(_ controller: EditProfileViewController) {
16        reloadProfile()
17    }
18
19    private func reloadProfile() {
20        print("Refresh profile data")
21    }
22}
23
24final class EditProfileViewController: UIViewController {
25    weak var delegate: EditProfileViewControllerDelegate?
26
27    @IBAction private func doneButtonTapped(_ sender: UIButton) {
28        delegate?.editProfileViewControllerDidFinish(self)
29        dismiss(animated: true)
30    }
31}

This pattern is easy to review and test. The presented controller says it finished, and the presenting controller updates itself in one place.

Handling Swipe-to-Dismiss on Modern Sheets

On current iOS versions, a sheet can disappear without the user tapping your own Done button. If the modal uses swipe-to-dismiss, you should also listen to UIPresentationController callbacks. The key method is presentationControllerDidDismiss(_:).

swift
1import UIKit
2
3final class SettingsViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
4    func showFilters() {
5        let filters = FiltersViewController()
6        filters.modalPresentationStyle = .pageSheet
7        filters.presentationController?.delegate = self
8        present(filters, animated: true)
9    }
10
11    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
12        applyUpdatedFilters()
13    }
14
15    private func applyUpdatedFilters() {
16        print("Filters reapplied after dismissal")
17    }
18}

This delegate fires after the interactive dismissal finishes. It is the right hook when you care about the modal disappearing regardless of whether the user tapped a button or swiped the sheet away.

Detecting Dismissal Inside the Presented Controller

Sometimes the presented controller itself needs to know that it is being removed, perhaps to stop a task or write a temporary draft. In that case, override viewDidDisappear(_:) and check isBeingDismissed.

swift
1override func viewDidDisappear(_ animated: Bool) {
2    super.viewDidDisappear(animated)
3
4    if isBeingDismissed {
5        print("The modal was dismissed")
6    }
7}

That check matters because viewDidDisappear(_:) also runs when another controller covers the screen. Without the extra guard, you may run cleanup at the wrong time.

Using a Dismiss Completion Block

If the presenting controller is the one calling dismiss(animated:completion:), the completion block can also be useful. That is a good place for small follow-up work that should run only after the animation has finished, such as reloading a table or starting a new presentation flow.

Common Pitfalls

A common mistake is relying only on the Done button path. With sheet presentations, users can often dismiss interactively, which bypasses your button handler. If you need guaranteed notification, add the presentation controller delegate as well.

Another problem is setting presentationController?.delegate too late. Configure it before presenting the controller so UIKit knows where to send dismissal events.

Be careful with memory management. Delegates should normally be weak, and closure callbacks should avoid strong reference cycles when they capture a presenting controller.

Finally, do not confuse dismissal with navigation popping. If the controller lives inside a navigation stack, isMovingFromParent is the property that tells you it was popped. isBeingDismissed is for modal dismissal.

Summary

  • Use a delegate or callback when the presented controller finishes through its own UI.
  • Add presentationControllerDidDismiss(_:) if the modal can be swiped away.
  • Use isBeingDismissed inside viewDidDisappear(_:) when the presented controller needs self-cleanup.
  • Set delegates before presentation and keep them weak.
  • Distinguish modal dismissal from navigation-stack removal.

Course illustration
Course illustration

All Rights Reserved.