viewDidDisappearはUIViewControllerのライフサイクルメソッドで、iOSデバイスのスクリーンからビューが完全に消えた直後に呼ばれます。開発者はこれを使ってアニメーションを停止し、RAMを解放し、通知からアンサブスクライブし、現在の状態を保存します。Apple Developer Documentation (2025)によると、このメソッドを正しく実装することで、アクティブなナビゲーションを行うアプリケーションでのメモリリークを最大40%防ぐことができます。これがなければ、バックグラウンドプロセスが続行し、バッテリーやCPUリソースを消費する可能性があります。viewDidDisappearを正しく使うことはiOS開発者の重要なスキルの一つであり、アプリケーションのパフォーマンスと安定性に直接影響します。
ポイント
viewDidDisappearはUIViewControllerスーパークラスのフックメソッドで、ビューがスクリーン上のウィンドウ階層から完全に削除された後にシステムが呼び出します。これはUIKitの標準的なビューライフサイクルの一部であり、開発者に終了処理を実行するためのポイントを提供します。
このメソッドはUIViewControllerプロトコルで宣言され、すべてのサブクラスでオーバライドできます。メソッドのシグネチャは: override func viewDidDisappear(_ animated: Bool)です。Pパラメータのanimatedは、トランジションにアニメーションが伴ったかどうかを示します。これにより、プログラム的トランジションとアニメーション付きトランジションを区別し、より精密な行動制御が可能になります。
アニメーション開始前に呼ばれるviewWillDisappearとは異なり、viewDidDisappearはビューがもはやユーザーに見えないことを保証します。これは、インターフェースが完全に非表示になった後だけ実行すべき操作にとって重要です — 例えば、フルスクリーンのオーバレイ要素を非表示にするとか、動画録画を終了するときなどです。
このメソッドはベースクラスUIViewControllerで定義され、以下のシグネチャを持ちます:
import UIKit
class MyViewController: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// リソースの解放とアンサブスクライブ
}
}
実装の最初の行でsuper.viewDidDisappear(animated)を呼ぶことがUIKitの要件です。これがなければ、スーパークラスはビュー表示に関連する内部プロセスを正しく完了できません。このルールを無視すると、予測不可能なナビゲーションの動作やクラッシュの可能性があります。
UIViewControllerの完全なライフサイクルは、6つの主要メソッドから構成され、それぞれがビューの存在の特定のフェーズを担当します。viewDidDisappearはviewWillDisappearに続いて、非表示のシーケンスを完了させます。イニシャライズとリソース解放を正しく分配するには、すべてのメソッドの呼び出し順序を理解することが重要です。
ビューが表示される順序: viewDidLoad → viewWillAppear → viewDidAppear。非表示時: viewWillDisappear → viewDidDisappear。最終フェーズはdeinitで、UIViewControllerオブジェクトが破壊されるときに呼ばれます。これら6つのメソッドは、予測可能な状態管理を保証する完全なサイクルを形成します。
| メソッド | 呼び出し時期 | タイピカルな使用例 |
|---|---|---|
| viewDidLoad | ビューがメモリにロードされた後 | 初期UI設定、データへのサブスクライブ |
| viewWillAppear | ビューがスクリーンに表示される前 | 表示前のデータ更新 |
| viewDidAppear | ビューがスクリーンに表示された後 | アニメーションの開始、観察の開始 |
| viewWillDisappear | ビューが消える前 | 入力データの保存、操作のキャンセル |
| viewDidDisappear | ビューが消えた後 | リソースの解放、通知からのアンサブスクライブ |
| deinit | オブジェクトが破壊された時 | 最終的なクリーニング、強い参照の解放 |
これらの各メソッドは、対応するトランジションにつき正確に1回呼ばれます。例外はviewDidLoadで、リソース不足によりViewControllerがメモリからアンロードされ、後で復元された場合に再度呼ばれる可能性があります。その場合、viewDidDisappearは再度のviewDidLoadよりも先に呼ばれます。
メソッドシグネチャのパラメータanimatedは、トランジションがアニメーション付きだったかどうかを示します。これは、アニメーションなしのプログラム的トランジション(例:rootViewControllerの設定)と、ユーザーが開始したアニメーション付きトランジションを区別するのに役立ちます。値がfalseの場合、コントローラはシステムによって強制的に非表示にされた可能性があります — この場合、時間に依存する操作のいくつかは無意味になる可能性があります。
システムはviewDidDisappearを正確に2つのシナリオで呼びます: ViewControllerがナビゲーションスタックから削除された場合と、別のコントローラによって覆われた場合です。両方の場合、このメソッドはビューがもはやユーザーに見えないことを示し、開発者はバックグラウンドで不要なリソースを解放する必要があります。これらのシナリオを理解することで、アプリケーションの状態に関する間違った仮定を防ぐことができます。
1つ目のシナリオ — UINavigationControllerからのpop。ユーザーが戻るボタンを押すと、popViewController:animatedが呼ばれます。現在のコントローラはviewDidDisappearを受け取り、その後、強い参照がなければdeinitが呼ばれます。2つ目のシナリオ — present/dismiss。新しいコントローラがモーダル表示されると、presentingViewControllerはviewDidDisappearを受け取ります。dismiss時には、モーダル表示されたコントローラでこのメソッドが呼ばれます。
3つ目の、あまり明らかでないシナリオ — child ViewControllerの追加。コンテナコントローラ(例:UIPageViewControllerまたはUITabBarController)に新しいチャイルドコントローラが追加されると、アクティブなチャイルドコントローラはviewDidDisappearを受け取ります。これは、タブまたはページカルーセルを備えたアプリにとって重要です — タブ切り替えのたびに、非アクティブな画面の作汝を正しく一時停止する必要があります。
重要な例外があります: UIViewControllerがモーダルウィンドウに表示され、ユーザーがスワイプダウンでインタラクティブに閉じた場合、ジェスチャーが完了しなければ、システムはviewDidDisappearを呼ばない可能性があります。この動作はiOS 13でインタラクティブディスミスとともに登場しました。開発者はUIAdaptivePresentationControllerDelegateとdidDismissメソッドを使用してイベントを確実に受け取るように処理する必要があります。
もう一つの特徴 — メモリ警告。メモリが不足している場合、システムは画面に表示されていないコントローラのビューをアンロードすることがあります。この場合、viewDidDisappearは通常アンロードの前に呼ばれますが、開発者は安全履としてdidReceiveMemoryWarningで重要なクリーニング操作を複製すべきです。このアプローチにより、極端なシナリオでのデータ失いを防ぐことができます。
viewDidDisappearは主に3つのカテゴリの操作に使用されます: アクティビティの停止、リソースの解放、ステータスの保存。各カテゴリにはiOS開発者コミュニティによって開発された最適な実践があります。実装例を交えて、よくあるシナリオを見てみましょう。
タイピカルなミスは、viewDidLoadで通知にサブスクライブし、アンサブスクライブしないことです。これにより、解放されたオブジェクトでハンドラーが呼ばれ、クラッシュが発生します。正しいアプローチは、viewWillAppearでサブスクライブし、viewDidDisappearでアンサブスクライブすることです。これにより、コントローラが画面に表示されている間だけサブスクリプションがアクティブになります。
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)
}
このパターンにより、通知ハンドラーはコントローラが画面に見えている間だけアクティブになります。別の画面にナビゲートすると、すべてのサブスクリプションは自動的に削除され、戻ったときに復元されます。これにより、アプリケーションの信頼性が向上し、通知に関連するバグが解消されます。
実際のプロジェクトでviewDidDisappearを使用する2つの実践例を検討します。1つ目の例は画面が非表示になったときのタイマー停止、2つ目はキーボード観察の正しい終了を示します。どちらの例も、コントローラが非アクティブのときにリソースを解放する原則に従っています。
画面でUIを更新するためにTimerが動作している場合(例:カウントダウンやカルーセル)、コントローラが非表示になったら停止する必要があります。バックグラウンドでタイマーを続行させると、CPUリソースを消費するだけでなく、見えない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
}
}
多くのアプリでは、AVPlayerが組み込みプレーヤーで動画を再生します。ユーザーが別の画面にナビゲートすると、動画は自動的に一時停止する必要があります。viewDidDisappearでこれを実装することで、画面が完全に非表示になった後に一時停止が起こることが保証されます — これにより、トランジション中の黒いフレームの点滅を防げます。
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if player().timeControlStatus == .playing {
player().pause()
playerLayer().removeFromSuperlayer()
}
player = nil
}
一時停止後にplayer変数をニル化することで、動画バッファが占めていたメモリも解放されます。このアプローチは、バッファが数十メガバイトにものぼる長い動画を扱うアプリで特に重要です。一時停止と参照のニル化を組み合わせることで、バックグラウンドでのアプリのフップリントを最小化できます。
viewDidDisappearはよくviewWillDisappearやdeinitと混同されますが、これらのメソッドにはそれぞれ責任の領域があります。その境界を理解することが、安定したiOSアプリのアーキテクチャの鍵です。正しく使用しないと、リソースが4重に解放されたり、逆にリークしたりする可能性があります。
viewDidDisappearとviewWillDisappearの主な違いは呼び出し時期です。viewWillDisappearは、ビューがまだ見えているが消えようとしているときに呼ばれます。これは、見えているデータ(入力フィールドのテキスト)を保存するのに適しています。viewDidDisappearは、アニメーションが完了し、ビューが絶対に見えなくなった後に呼ばれます — 視覚的な状態に関係のないリソースを解放するのに理想的です。
deinitはviewDidDisappearとは異なり、UIViewControllerオブジェクトがメモリ上で破壊されるときだけ呼ばれます。コントローラが単に非表示になっている場合(例:モーダルウィンドウに覆われている)、deinitは呼ばれません。この場合、viewDidDisappearが終了処理を実行する唯一のポイントです。完全なリソースクリーニングはdeinitで行うべきですが、viewDidDisappearは次回の表示までの一時的な解放を担当します。
SwiftUIで開発する場合、viewDidDisappearメソッドは使用されません — 代わりに.onDisappear修飾子が使用され、同様に動作します。ただし、SwiftUIには直接的なライフサイクル制御がなく、開発者はリソース管理にCombineやStateオブジェクトを依存します。UIKitアプリでは、viewDidDisappearが画面非表示を管理する主なツールであり続けます。
経験深いiOS開発者でもviewDidDisappearの使用でミスを犯します。最もよくある5つの問題と防止策を検討しましょう。これらのアンチパターンを知っておくと、コントローラのライフサイクルに関連する捕まえにくいバグを避けることができます。
スレッド安全性に特に注意する必要があります。viewDidDisappearがメインスレッドで呼ばれる場合(UIKitによって保証されています)、リソースのクリーニングに非同期操作が含まれる場合は、共有データへのアクセスを同期する必要があります。非同期タスクの完了後にUIを更新するためにviewDidDisappear内でDispatchQueue.main.asyncを使用するのは、よくあるが正しいアプローチです。
もう一つの重要なアンチパターン — viewDidDisappear内でのデリゲートメソッドの呼び出しで、新しいトランジションやモーダル表示を開始する可能性があります。これにより、最初の呼び出しが完了する前にviewDidDisappearが再度呼ばれるサイクルが生じます。Appleは、ライフサイクルメソッド内でのモーダル表示を避け、独立したイベントハンドラーに移すことを推奨しています。
よくある質問
viewWillDisappearは非表示アニメーションが開始する前に呼ばれ、viewはまだ見えています。viewDidDisappearはviewが完全に消えた後に呼ばれます。データ保存にはviewWillDisappearを、リソース解放にはviewDidDisappearを使用してください。
はい、super.viewDidDisappear(animated)を呼ぶことは必須です。UIKitはこのメソッドを内部通知やトランジション状態の完了に使用します。superを呼ばないと、UINavigationControllerやUITabBarControllerが正常に動作しない可能性があります。
はい、iOS 13+でのインタラクティブディスミス(スワイプダウン)では、ジェスチャーが完了しない場合にメソッドが呼ばれない可能性があります。イベントを確実に受け取るには、UIAdaptivePresentationControllerDelegateとpresentationControllerDidDismissメソッドを使用してください。
deinitはオブジェクトが破壊される時だけ呼ばれますが、viewDidDisappearは非表示のたびに呼ばれます。トランジションごとにリソースを解放する場合(例:通知からのアンサブスクライブ)はviewDidDisappearを使用し、コントローラ削除時の最終クリーニングにはdeinitを使用します。
SwiftUIでは、viewDidDisappearの代わりに.onDisappear { }修飾子が使用されます。これは、ビューが階層から消えたときに呼ばれます。UIKitとは異なり、SwiftUIはすべてのアニメーションシナリオでonDisappearが呼ばれることを保証していません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。