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 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.
The method is defined in the base class UIViewController and has the following signature:
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.
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: viewDidLoad → viewWillAppear → viewDidAppear. When hiding: viewWillDisappear → viewDidDisappear. 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.
| Method | Call moment | Typical usage |
|---|---|---|
| viewDidLoad | After loading the view into memory | Initial UI setup, subscribing to data |
| viewWillAppear | Before the view appears on screen | Updating data before display |
| viewDidAppear | After the view appears on screen | Starting animations, beginning observation |
| viewWillDisappear | Before the view disappears | Saving input data, canceling operations |
| viewDidDisappear | After the view disappears | Freeing resources, unsubscribing from notifications |
| deinit | When the object is destroyed | Final 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.
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.
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.
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.
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.
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.
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.
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.
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.
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
}
}
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.
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 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 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.
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.
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
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.
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.
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.
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.
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
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.
Read also