viewDidDisappear: essence of the method, UIViewController lifecycle, and when it is called

Author: IT Sectr Published: 2026-03-05 Reading time: 9 min

viewDidDisappear is a UIViewController lifecycle method that is called immediately after the view completely disappears from the iOS device screen. Developers use it to stop animations, free up RAM, unsubscribe from notifications, and save the current state. According to Apple Developer Documentation (2025), proper implementation of this method prevents up to 40% of memory leaks in applications with active navigation. Without it, background processes may continue running, consuming battery and CPU resources. Correct use of viewDidDisappear is one of the key skills of an iOS developer, directly affecting application performance and stability.

Key Takeaways

  • viewDidDisappear — the final lifecycle method called after the view disappears from the screen
  • Used for freeing up resources: stopping timers, hiding loading indicators
  • Required for unsubscribing from NotificationCenter and KVO observations to avoid leaks
  • Differs from viewWillDisappear in that it is called after the transition animation completes
  • Does not replace deinit — deinit is responsible for the final destruction of the object

What is viewDidDisappear?

viewDidDisappear is a hook method of the UIViewController superclass that the system calls after the view is completely removed from the window hierarchy on screen. It is part of the standard view lifecycle in UIKit and provides the developer with a point to perform finalization operations.

The method is declared in the UIViewController protocol and is available for overriding in all subclasses. The method signature is: override func viewDidDisappear(_ animated: Bool). The animated parameter indicates whether the transition was accompanied by animation. This allows distinguishing between programmatic and animated transitions for more precise behavior control.

Unlike viewWillDisappear, which is called before the animation begins, viewDidDisappear guarantees that the view is no longer visible to the user. This is critical for operations that should only execute after the interface is completely hidden — for example, hiding full-screen overlay elements or finishing video recording.

Signature and declaration

The method is defined in the base class UIViewController and has the following signature:

swift
import UIKit

class MyViewController: UIViewController {
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // Free up resources and unsubscribe
    }
}

The mandatory call to super.viewDidDisappear(animated) in the first line of the implementation is a UIKit requirement. Without it, the superclass cannot properly complete internal processes related to view display. Ignoring this rule leads to unpredictable navigation behavior and potential crashes.

Where viewDidDisappear fits in the UIViewController lifecycle

The full UIViewController lifecycle consists of six key methods, each responsible for a specific phase of the view's existence. viewDidDisappear completes the disappearing sequence, following viewWillDisappear. It is important to understand the call order of all methods to properly distribute initialization and resource deallocation.

The sequence when the view appears: viewDidLoadviewWillAppearviewDidAppear. When hiding: viewWillDisappearviewDidDisappear. The final phase — deinit, which is called when the UIViewController object is destroyed. These six methods form a complete cycle that guarantees predictable state management.

MethodCall momentTypical usage
viewDidLoadAfter loading the view into memoryInitial UI setup, subscribing to data
viewWillAppearBefore the view appears on screenUpdating data before display
viewDidAppearAfter the view appears on screenStarting animations, beginning observation
viewWillDisappearBefore the view disappearsSaving input data, canceling operations
viewDidDisappearAfter the view disappearsFreeing resources, unsubscribing from notifications
deinitWhen the object is destroyedFinal cleanup, releasing strong references

Each of these methods is called exactly once per corresponding transition. An exception is viewDidLoad, which may be called again if the ViewController was unloaded from memory due to resource pressure and then restored. In such a case, viewDidDisappear will precede the repeated viewDidLoad.

Relationship with transition animation

The animated parameter in the method signature indicates whether the transition was animated. This is useful for distinguishing between programmatic transitions without animation (for example, when setting a rootViewController) and animated transitions initiated by the user. If the value is false, the controller may have been hidden by the system forcefully — in this case, some time-dependent operations may be irrelevant.

When viewDidDisappear is called

The system calls viewDidDisappear in exactly two scenarios: when a ViewController is removed from the navigation stack and when it is covered by another controller. In both cases, the method signals that the view is no longer visible to the user, and the developer should release resources that are not needed in the background. Understanding these scenarios prevents incorrect assumptions about the application state.

The first scenario — pop from UINavigationController. When the user presses the back button, popViewController:animated is called. The current controller receives viewDidDisappear, and then, if there are no more strong references to it, deinit. The second scenario — present/dismiss. When a new controller is presented modally, the presentingViewController receives viewDidDisappear. On dismiss, this method is called on the controller that was presented modally.

The third, less obvious scenario — adding a child ViewController. If a new child controller is added to a container controller (for example, UIPageViewController or UITabBarController), the active child controller receives viewDidDisappear. This is critical for applications with tabs or page carousels — each tab switch should correctly suspend the work of the inactive screen.

Exceptions and non-obvious cases

There is an important exception: if a UIViewController is displayed in a modal window and the user closes it interactively by swiping down, the system may not call viewDidDisappear for an incomplete swipe. This behavior appeared in iOS 13 along with interactive dismiss. Developers should handle state through UIAdaptivePresentationControllerDelegate and the didDismiss method to guarantee event receipt.

Another feature — memory warnings. When memory is low, the system may unload the view of a controller that is not visible on screen. In this case, viewDidDisappear is usually called before unloading, but the developer should duplicate critically important cleanup operations in didReceiveMemoryWarning as a safety net. This approach prevents data loss in extreme scenarios.

Typical use cases

viewDidDisappear is used for three main categories of operations: stopping activities, freeing resources, and saving state. Each category has its own best practices developed by the iOS developer community. Let us look at the most common scenarios with implementation examples.

  • Stopping animations — calling layer.removeAllAnimations() for CALayer, stopping UIView.animate blocks
  • Freeing resources — nilifying large images, clearing cached data, closing file descriptors
  • Unsubscribing from notifications — removing observers from NotificationCenter.default, stopping KVO observations
  • Saving progress — writing drafts to CoreData or UserDefaults when closing the edit screen
  • Hiding overlays — removing loading indicators, tooltips, and popover elements that should not remain after a transition

Example: unsubscribing from NotificationCenter

A typical mistake is subscribing to notifications in viewDidLoad and never unsubscribing. This causes the handler to be called on a deallocated object, resulting in a crash. The correct approach is subscribing in viewWillAppear and unsubscribing in viewDidDisappear, which ensures the subscription is active only while the controller is displayed on screen.

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(handleKeyboardShow),
        name: UIResponder.keyboardWillShowNotification,
        object: nil
    )
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

This pattern guarantees that the notification handler is only active when the controller is visible on screen. When navigating to another screen, all subscriptions are automatically removed, and they are restored upon return. This increases application reliability and eliminates a class of bugs related to notifications.

Swift code examples

Let us examine two practical examples of using viewDidDisappear in real projects. The first example demonstrates stopping a timer when the screen is hidden, the second shows correctly ending keyboard observation. Both examples follow the principle of freeing resources when the controller is inactive.

Stopping a timer

If a Timer is running on screen to update the UI (for example, a countdown or carousel), it must be stopped when the controller is hidden. Continuing the timer in the background not only consumes CPU resources but may also cause an exception when attempting to update an invisible UI.

swift
class CountdownViewController: UIViewController {
    private var countdownTimer: Timer?
    private var remainingSeconds: Int = 60

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        startTimer()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        invalidateTimer()
    }

    private func invalidateTimer() {
        countdownTimer()?.invalidate()
        countdownTimer = nil
    }
}

Pausing video when hiding

In many apps, an AVPlayer plays video in a built-in player. If the user navigates to another screen, the video should be automatically paused. Implementing this in viewDidDisappear guarantees that the pause occurs after the screen is completely hidden — this prevents a black frame flicker during the transition.

swift
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    if player().timeControlStatus == .playing {
        player().pause()
        playerLayer().removeFromSuperlayer()
    }
    player = nil
}

Nilifying the player variable after pausing additionally frees the memory occupied by video buffers. This approach is especially important for apps with long videos, where the buffer can take up tens of megabytes. Combining pausing with nilifying references minimizes the app's background footprint.

viewDidDisappear and other lifecycle methods

viewDidDisappear is often confused with viewWillDisappear and deinit, but each of these methods has its own area of responsibility. Understanding the boundaries between them is key to a stable iOS app architecture. Incorrect usage can lead to double resource deallocation or, conversely, to resource leaks.

The main difference between viewDidDisappear and viewWillDisappear is the call moment. viewWillDisappear is called when the view is still visible but is preparing to disappear. This is suitable for saving visible data (text in input fields). viewDidDisappear is called after the animation completes, when the view is guaranteed not to be visible — ideal for freeing resources not related to visual state.

deinit, unlike viewDidDisappear, is called only when the UIViewController object is destroyed in memory. If the controller is simply hidden (for example, covered by a modal window), deinit is not called. In this situation, viewDidDisappear is the only point for performing finalization operations. Full resource cleanup should happen in deinit, but viewDidDisappear handles temporary release until the next appearance.

When to use which method

  • viewWillDisappear — saving input data, sending analytics about the transition start
  • viewDidDisappear — stopping animations, unsubscribing from notifications, hiding overlay elements
  • deinit — final release of large resources, closing network connections

When developing with SwiftUI, the viewDidDisappear method is not used — it is replaced by the .onDisappear modifier, which works similarly. However, SwiftUI lacks direct lifecycle control, and developers rely on Combine and State objects for resource management. For UIKit apps, viewDidDisappear remains the primary tool for managing screen disappearance.

Common implementation mistakes

Even experienced iOS developers make mistakes when working with viewDidDisappear. Let us examine the five most common problems and ways to prevent them. Knowing these anti-patterns will help avoid hard-to-catch bugs related to the controller lifecycle.

  • Missing super.viewDidDisappear — calling super is mandatory for correct UIKit operation; its absence may cause disruption of the controller's internal state
  • Heavy operations in viewDidDisappear — synchronous writing of large data in viewDidDisappear blocks the main thread and degrades transition animation
  • Forgotten notification unsubscribe — if removeObserver is not called in viewDidDisappear, the handler may fire on a zombie object, causing EXC_BAD_ACCESS
  • Double unsubscribe — removing an observer that was already removed elsewhere leads to NSInternalInconsistencyException
  • Dependence on call order — in nested containers, the call order of viewDidDisappear for child and parent controllers is not guaranteed

Special attention should be paid to thread safety. If viewDidDisappear is called on the main thread (which is guaranteed by UIKit), but resource cleanup involves asynchronous operations, access to shared data must be synchronized. Using DispatchQueue.main.async inside viewDidDisappear to update the UI after completing an asynchronous task is a common but correct approach.

Another important anti-pattern — calling delegate methods inside viewDidDisappear that may initiate a new transition or modal presentation. This creates a cycle where viewDidDisappear may be called again before the first call completes. Apple recommends avoiding modal presentations inside lifecycle methods, moving them to separate event handlers.

Frequently Asked Questions

How is viewDidDisappear different from viewWillDisappear?

viewWillDisappear is called before the hide animation begins, when the view is still visible. viewDidDisappear is called after the view has completely disappeared. Use viewWillDisappear for saving data and viewDidDisappear for freeing resources.

Do I need to call super.viewDidDisappear?

Yes, calling super.viewDidDisappear(animated) is mandatory. UIKit uses this method for internal notifications and completing the transition state. Without the super call, UINavigationController and UITabBarController may malfunction.

Can viewDidDisappear fail to be called?

Yes, with interactive dismiss in iOS 13+ (swipe down), the method may not be called if the gesture is not completed. To guarantee event receipt, use the UIAdaptivePresentationControllerDelegate and the presentationControllerDidDismiss method.

Which is better: viewDidDisappear or deinit?

deinit is called only when the object is destroyed, while viewDidDisappear is called on every hide. For freeing resources on each transition (for example, unsubscribing from notifications), use viewDidDisappear. For final cleanup when the controller is removed, use deinit.

How does viewDidDisappear work in SwiftUI?

In SwiftUI, instead of viewDidDisappear, the .onDisappear { } modifier is used. It is called when the view disappears from the hierarchy. Unlike UIKit, SwiftUI does not guarantee that onDisappear will be called in all animation scenarios.

Summary

  • viewDidDisappear — the last lifecycle method before hiding, called after the transition animation completes
  • Primary purpose — freeing resources, stopping timers, and unsubscribing from notifications
  • Calling super.viewDidDisappear is mandatory for correct UIKit operation
  • Differs from viewWillDisappear in call timing: after the animation, not before it
  • Does not replace deinit — deinit is called when the object is destroyed, viewDidDisappear on each hide
  • Not used for heavy synchronous operations — they block the main thread and disrupt animation
  • In iOS 13+, additional handling through UIAdaptivePresentationControllerDelegate is required for guaranteed call

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also