viewDidDisappear: メソッドの本質、UIViewControllerのライフサイクル、そして呼ばれるタイミング

著者: IT Sectr 公開日: 2026-03-05 読了時間: 9 分

viewDidDisappearはUIViewControllerのライフサイクルメソッドで、iOSデバイスのスクリーンからビューが完全に消えた直後に呼ばれます。開発者はこれを使ってアニメーションを停止し、RAMを解放し、通知からアンサブスクライブし、現在の状態を保存します。Apple Developer Documentation (2025)によると、このメソッドを正しく実装することで、アクティブなナビゲーションを行うアプリケーションでのメモリリークを最大40%防ぐことができます。これがなければ、バックグラウンドプロセスが続行し、バッテリーやCPUリソースを消費する可能性があります。viewDidDisappearを正しく使うことはiOS開発者の重要なスキルの一つであり、アプリケーションのパフォーマンスと安定性に直接影響します。

ポイント

  • viewDidDisappear — スクリーンからビューが消えた後に呼ばれる最終的なライフサイクルメソッド
  • リソースの解放に使用:タイマーの停止、ローディングインジケータの非表示
  • リークを避けるためにNotificationCenterからのアンサブスクライブとKVO観察が必要
  • トランジションアニメーションが完了した後に呼ばれる点がviewWillDisappearと異なる
  • deinitを置き換えるわけではない — deinitはオブジェクトの最終的な破壊を担当する

viewDidDisappearとは?

viewDidDisappearはUIViewControllerスーパークラスのフックメソッドで、ビューがスクリーン上のウィンドウ階層から完全に削除された後にシステムが呼び出します。これはUIKitの標準的なビューライフサイクルの一部であり、開発者に終了処理を実行するためのポイントを提供します。

このメソッドはUIViewControllerプロトコルで宣言され、すべてのサブクラスでオーバライドできます。メソッドのシグネチャは: override func viewDidDisappear(_ animated: Bool)です。Pパラメータのanimatedは、トランジションにアニメーションが伴ったかどうかを示します。これにより、プログラム的トランジションとアニメーション付きトランジションを区別し、より精密な行動制御が可能になります。

アニメーション開始前に呼ばれるviewWillDisappearとは異なり、viewDidDisappearはビューがもはやユーザーに見えないことを保証します。これは、インターフェースが完全に非表示になった後だけ実行すべき操作にとって重要です — 例えば、フルスクリーンのオーバレイ要素を非表示にするとか、動画録画を終了するときなどです。

シグネチャと宣言

このメソッドはベースクラスUIViewControllerで定義され、以下のシグネチャを持ちます:

swift
import UIKit

class MyViewController: UIViewController {
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // リソースの解放とアンサブスクライブ
    }
}

実装の最初の行でsuper.viewDidDisappear(animated)を呼ぶことがUIKitの要件です。これがなければ、スーパークラスはビュー表示に関連する内部プロセスを正しく完了できません。このルールを無視すると、予測不可能なナビゲーションの動作やクラッシュの可能性があります。

UIViewControllerのライフサイクルにおけるviewDidDisappearの位置付け

UIViewControllerの完全なライフサイクルは、6つの主要メソッドから構成され、それぞれがビューの存在の特定のフェーズを担当します。viewDidDisappearはviewWillDisappearに続いて、非表示のシーケンスを完了させます。イニシャライズとリソース解放を正しく分配するには、すべてのメソッドの呼び出し順序を理解することが重要です。

ビューが表示される順序: viewDidLoadviewWillAppearviewDidAppear。非表示時: viewWillDisappearviewDidDisappear。最終フェーズはdeinitで、UIViewControllerオブジェクトが破壊されるときに呼ばれます。これら6つのメソッドは、予測可能な状態管理を保証する完全なサイクルを形成します。

メソッド呼び出し時期タイピカルな使用例
viewDidLoadビューがメモリにロードされた後初期UI設定、データへのサブスクライブ
viewWillAppearビューがスクリーンに表示される前表示前のデータ更新
viewDidAppearビューがスクリーンに表示された後アニメーションの開始、観察の開始
viewWillDisappearビューが消える前入力データの保存、操作のキャンセル
viewDidDisappearビューが消えた後リソースの解放、通知からのアンサブスクライブ
deinitオブジェクトが破壊された時最終的なクリーニング、強い参照の解放

これらの各メソッドは、対応するトランジションにつき正確に1回呼ばれます。例外はviewDidLoadで、リソース不足によりViewControllerがメモリからアンロードされ、後で復元された場合に再度呼ばれる可能性があります。その場合、viewDidDisappearは再度のviewDidLoadよりも先に呼ばれます。

トランジションアニメーションとの関係

メソッドシグネチャのパラメータanimatedは、トランジションがアニメーション付きだったかどうかを示します。これは、アニメーションなしのプログラム的トランジション(例:rootViewControllerの設定)と、ユーザーが開始したアニメーション付きトランジションを区別するのに役立ちます。値がfalseの場合、コントローラはシステムによって強制的に非表示にされた可能性があります — この場合、時間に依存する操作のいくつかは無意味になる可能性があります。

viewDidDisappearが呼ばれるタイミング

システムは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開発者コミュニティによって開発された最適な実践があります。実装例を交えて、よくあるシナリオを見てみましょう。

  • アニメーションの停止 — CALayerでのlayer.removeAllAnimations()の呼び出し、UIView.animateブロックの停止
  • リソースの解放 — 大きな画像のニル化、キャッシュデータのクリア、ファイルディスクリプタの閉鎖
  • 通知からのアンサブスクライブ — NotificationCenter.defaultからのオブザーバー削除、KVO観察の停止
  • 進捗の保存 — 編集画面を閉じる際にCoreDataまたはUserDefaultsにドラフトを書き込む
  • オーバレイの非表示 — トランジション後に残ってはならないローディングインジケータ、ツールチップ、ポップオーバー要素の削除

例: NotificationCenterからのアンサブスクライブ

タイピカルなミスは、viewDidLoadで通知にサブスクライブし、アンサブスクライブしないことです。これにより、解放されたオブジェクトでハンドラーが呼ばれ、クラッシュが発生します。正しいアプローチは、viewWillAppearでサブスクライブし、viewDidDisappearでアンサブスクライブすることです。これにより、コントローラが画面に表示されている間だけサブスクリプションがアクティブになります。

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)
}

このパターンにより、通知ハンドラーはコントローラが画面に見えている間だけアクティブになります。別の画面にナビゲートすると、すべてのサブスクリプションは自動的に削除され、戻ったときに復元されます。これにより、アプリケーションの信頼性が向上し、通知に関連するバグが解消されます。

Swiftコード例

実際のプロジェクトでviewDidDisappearを使用する2つの実践例を検討します。1つ目の例は画面が非表示になったときのタイマー停止、2つ目はキーボード観察の正しい終了を示します。どちらの例も、コントローラが非アクティブのときにリソースを解放する原則に従っています。

タイマーの停止

画面でUIを更新するためにTimerが動作している場合(例:カウントダウンやカルーセル)、コントローラが非表示になったら停止する必要があります。バックグラウンドでタイマーを続行させると、CPUリソースを消費するだけでなく、見えない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
    }
}

非表示時の動画一時停止

多くのアプリでは、AVPlayerが組み込みプレーヤーで動画を再生します。ユーザーが別の画面にナビゲートすると、動画は自動的に一時停止する必要があります。viewDidDisappearでこれを実装することで、画面が完全に非表示になった後に一時停止が起こることが保証されます — これにより、トランジション中の黒いフレームの点滅を防げます。

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

一時停止後にplayer変数をニル化することで、動画バッファが占めていたメモリも解放されます。このアプローチは、バッファが数十メガバイトにものぼる長い動画を扱うアプリで特に重要です。一時停止と参照のニル化を組み合わせることで、バックグラウンドでのアプリのフップリントを最小化できます。

viewDidDisappearと他のライフサイクルメソッド

viewDidDisappearはよくviewWillDisappearやdeinitと混同されますが、これらのメソッドにはそれぞれ責任の領域があります。その境界を理解することが、安定したiOSアプリのアーキテクチャの鍵です。正しく使用しないと、リソースが4重に解放されたり、逆にリークしたりする可能性があります。

viewDidDisappearとviewWillDisappearの主な違いは呼び出し時期です。viewWillDisappearは、ビューがまだ見えているが消えようとしているときに呼ばれます。これは、見えているデータ(入力フィールドのテキスト)を保存するのに適しています。viewDidDisappearは、アニメーションが完了し、ビューが絶対に見えなくなった後に呼ばれます — 視覚的な状態に関係のないリソースを解放するのに理想的です。

deinitはviewDidDisappearとは異なり、UIViewControllerオブジェクトがメモリ上で破壊されるときだけ呼ばれます。コントローラが単に非表示になっている場合(例:モーダルウィンドウに覆われている)、deinitは呼ばれません。この場合、viewDidDisappearが終了処理を実行する唯一のポイントです。完全なリソースクリーニングはdeinitで行うべきですが、viewDidDisappearは次回の表示までの一時的な解放を担当します。

各メソッドを使うタイミング

  • viewWillDisappear — 入力データの保存、トランジション開始に関するアナリティクスの送信
  • viewDidDisappear — アニメーションの停止、通知からのアンサブスクライブ、オーバレイ要素の非表示
  • deinit — 大きなリソースの最終解放、ネットワーク接続の閉鎖

SwiftUIで開発する場合、viewDidDisappearメソッドは使用されません — 代わりに.onDisappear修飾子が使用され、同様に動作します。ただし、SwiftUIには直接的なライフサイクル制御がなく、開発者はリソース管理にCombineやStateオブジェクトを依存します。UIKitアプリでは、viewDidDisappearが画面非表示を管理する主なツールであり続けます。

実装中の一般的なミス

経験深いiOS開発者でもviewDidDisappearの使用でミスを犯します。最もよくある5つの問題と防止策を検討しましょう。これらのアンチパターンを知っておくと、コントローラのライフサイクルに関連する捕まえにくいバグを避けることができます。

  • super.viewDidDisappearの省略 — UIKitが正しく動作するにはsuperの呼び出しが必須。これがないと、コントローラの内部状態が正常に維持されない可能性があります
  • viewDidDisappearでの重い処理 — viewDidDisappearでの大きなデータの同期書き込みは、メインスレッドをブロックし、トランジションアニメーションを悪化させます
  • 通知アンサブスクライブの忘れ — viewDidDisappearでremoveObserverを呼ばないと、ハンドラーがゾンビーオブジェクトで発火し、EXC_BAD_ACCESSが発生する
  • 亍重のアンサブスクライブ — すでに別の場所で削除されたオブザーバーを削除すると、NSInternalInconsistencyExceptionが発生する
  • 呼び出し順序への依存 — ネストされたコンテナでは、チャイルドとペアレントコントローラのviewDidDisappearの呼び出し順序は保証されない

スレッド安全性に特に注意する必要があります。viewDidDisappearがメインスレッドで呼ばれる場合(UIKitによって保証されています)、リソースのクリーニングに非同期操作が含まれる場合は、共有データへのアクセスを同期する必要があります。非同期タスクの完了後にUIを更新するためにviewDidDisappear内でDispatchQueue.main.asyncを使用するのは、よくあるが正しいアプローチです。

もう一つの重要なアンチパターン — viewDidDisappear内でのデリゲートメソッドの呼び出しで、新しいトランジションやモーダル表示を開始する可能性があります。これにより、最初の呼び出しが完了する前にviewDidDisappearが再度呼ばれるサイクルが生じます。Appleは、ライフサイクルメソッド内でのモーダル表示を避け、独立したイベントハンドラーに移すことを推奨しています。

よくある質問

viewDidDisappearとviewWillDisappearの違いは?

viewWillDisappearは非表示アニメーションが開始する前に呼ばれ、viewはまだ見えています。viewDidDisappearはviewが完全に消えた後に呼ばれます。データ保存にはviewWillDisappearを、リソース解放にはviewDidDisappearを使用してください。

super.viewDidDisappearを呼ぶ必要はありますか?

はい、super.viewDidDisappear(animated)を呼ぶことは必須です。UIKitはこのメソッドを内部通知やトランジション状態の完了に使用します。superを呼ばないと、UINavigationControllerやUITabBarControllerが正常に動作しない可能性があります。

viewDidDisappearが呼ばれないことはありますか?

はい、iOS 13+でのインタラクティブディスミス(スワイプダウン)では、ジェスチャーが完了しない場合にメソッドが呼ばれない可能性があります。イベントを確実に受け取るには、UIAdaptivePresentationControllerDelegateとpresentationControllerDidDismissメソッドを使用してください。

viewDidDisappearとdeinitのどちらがいいですか?

deinitはオブジェクトが破壊される時だけ呼ばれますが、viewDidDisappearは非表示のたびに呼ばれます。トランジションごとにリソースを解放する場合(例:通知からのアンサブスクライブ)はviewDidDisappearを使用し、コントローラ削除時の最終クリーニングにはdeinitを使用します。

SwiftUIでviewDidDisappearはどのように動作しますか?

SwiftUIでは、viewDidDisappearの代わりに.onDisappear { }修飾子が使用されます。これは、ビューが階層から消えたときに呼ばれます。UIKitとは異なり、SwiftUIはすべてのアニメーションシナリオでonDisappearが呼ばれることを保証していません。

まとめ

  • viewDidDisappear — 非表示前の最終ライフサイクルメソッド、トランジションアニメーション完了後に呼ばれる
  • 主な目的 — リソースの解放、タイマーの停止、通知からのアンサブスクライブ
  • UIKitが正しく動作するにはsuper.viewDidDisappearの呼び出しが必須
  • 呼び出し時期がviewWillDisappearと異なる: アニメーション後であってその前ではない
  • deinitを置き換えるわけではない — deinitはオブジェクト破壊時、viewDidDisappearは非表示のたびに呼ばれる
  • 重い同期処理には使用しない — メインスレッドをブロックし、アニメーションを悪化させる
  • iOS 13+では、確実に呼び出すためにUIAdaptivePresentationControllerDelegateを介した追加処理が必要

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください