はじめに (対象読者・この記事でわかること)
この記事は、iOS アプリ開発を行っているエンジニア、特に UIKit を使って UIViewController のライフサイクルを扱う初心者から中級者を対象としています。実装途中で「viewWillAppear が呼び出されない」現象に直面したときに、原因の切り分け方や正しい対処方法がすぐに分かるようになることを目的としています。この記事を読むことで、以下が可能になります。
- viewWillAppear が実行されない典型的なパターンを把握できる
- 正しいオーバーライドの書き方と、Storyboard/コードでの注意点を学べる
- デバッグ時に有効なログ出力やブレークポイントの設定方法が身につく
本テーマは、実務でタイミング依存のバグに悩まされることが多く、時間ロスを減らすためにも知っておきたい重要な知識です。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- Swift の基本的な文法(変数宣言、クラス定義、メソッドオーバーライド)
- UIKit の基本構造(UIViewController のライフサイクル、Storyboard とコードベースの UI 作成)
- Xcode のデバッグ機能(コンソール出力、ブレークポイント設定)
viewWillAppearが呼び出されない原因と背景
viewWillAppear(_:) は、UIViewController が画面に表示される直前にシステムが自動的に呼び出すメソッドです。通常は override func viewWillAppear(_ animated: Bool) と記述すれば確実に実行されますが、実際の開発現場では次のようなケースで呼び出しが失われることがあります。
- メソッドシグネチャのミス
-viewWillAppearの引数名や型が正しくない、overrideを付け忘れる、func viewWillAppear()のように引数を省くと、オーバーライドが成立せずスーパークラスの実装が呼ばれません。 - ビューコントローラの階層構造が期待と違う
- UINavigationController の内部でプッシュした際や、タブバーの切り替え時に、実際に表示されるのは子ビューコントローラではなく、ラッパーのコンテナビューコントローラになることがあります。この場合、子ビューコントローラのviewWillAppearが呼ばれず、代わりにコンテナ側のviewWillAppearが実行されます。 - Storyboard の「Is Initial View Controller」設定ミス
- 起動時に期待した ViewController がロードされず、別のインスタンスが表示されるケースです。結果として、デバッグ対象としているクラスのviewWillAppearが実行されないことがあります。 present(_:animated:completion:)の misuse
- モーダル遷移でpresentの直後にdismissを呼び出したり、animated: falseで連続遷移させると、表示タイミングがずれ、viewWillAppearがスキップされることがあります。- カスタム遷移アニメーションやトランジションデリゲート
-UIViewControllerTransitioningDelegateを実装している場合、animateTransition(using:)内で手動でviewWillAppear相当の処理を書かないとシステムが自動で呼び出さないことがあります。
以上のポイントを押さえておけば、viewWillAppear が呼び出されない現象を素早く切り分けられます。次のセクションでは、実際に「何が問題か」を特定し、コード例と共に確実に動作させる手順を示します。
具体的な対処手順と実装例
ここでは、上記で挙げた典型的な原因別に、実装上のチェックポイントと修正例を順を追って解説します。実際のプロジェクトでよく遭遇するパターンを中心に、コードサンプルとデバッグのヒントを交えて説明します。
ステップ1 シグネチャの正確性を確認する
まずは最も基本的なチェックから始めます。viewWillAppear のシグネチャは以下の通りです。
Swiftoverride func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // ここに処理を書く }
注意点
overrideキーワードは必須です。抜けているとコンパイルは通りますが、スーパークラスのメソッドをオーバーライドできず、期待通りに呼び出されません。- パラメータは必ず
(_ animated: Bool)の形で、名前は_(外部名なし)にします。 super.viewWillAppear(animated)の呼び出しを忘れないこと。これを省くと、内部で行われているレイアウトや状態保存が実行されず、予期せぬ挙動の原因になります。
デバッグ方法
print("viewWillAppear called") を追加し、コンソールに出力が入るか確認します。出力が無ければシグネチャが誤っている可能性が高いです。
Swiftoverride func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) print("[DEBUG] MyViewController viewWillAppear called") }
ステップ2 ビューコントローラ階層を意識した遷移実装
次に、遷移先が想定通りのインスタンスかどうかを検証します。特に UINavigationController や UITabBarController を利用している場合は、実際に表示されるビューコントローラが子クラスかどうかを確認します。
例:UINavigationController でのプッシュ
Swift// 正しいプッシュ例 if let vc = storyboard?.instantiateViewController(withIdentifier: "DetailVC") as? DetailViewController { navigationController?.pushViewController(vc, animated: true) }
問題が起きやすいパターン
pushViewControllerの代わりにpresentを誤って使用すると、モーダル遷移になるためviewWillAppearの呼び出しタイミングが変わります。popViewController後にすぐ別のpushを行うと、前のビューのviewWillDisappearが完了していない状態でviewWillAppearがスキップされることがあります。
デバッグヒント
遷移直前に次のコードで実際に遷移先オブジェクトを出力します。
Swiftprint("[DEBUG] Pushing view controller: \(vc)")
さらに、viewDidLoad と viewWillAppear の両方にログを入れ、ロード順序を視覚化すると原因が見つかりやすくなります。
ハマった点やエラー解決
ケースA:Storyboard の初期ビューコントローラが意図したものと違う
症状
アプリ起動時に viewWillAppear が全く呼び出されず、画面は別のビューが表示される。
原因
Storyboard 上で「Is Initial View Controller」のチェックが別のシーンに付いている。
解決策
1. Xcode の Interface Builder で対象の ViewController を選択する。
2. Attributes Inspector の「Is Initial View Controller」チェックを正しいシーンに付け直す。
3. AppDelegate/SceneDelegate の window?.rootViewController 設定が手動で上書きされていないか確認する。
ケースB:present(_:animated:completion:) の連続呼び出しで viewWillAppear がスキップ
症状
モーダル画面を表示した直後に別のモーダル画面を連続で表示すると、2 番目の画面の viewWillAppear が呼ばれない。
原因
iOS は表示アニメーションが完了するまで次の遷移をキューイングしないため、present の直後に別の present を呼び出すと内部で状態が不整合になる。
解決策
present の completion クロージャ内で次の遷移を実行する。
Swiftpresent(firstVC, animated: true) { self.present(secondVC, animated: true, completion: nil) }
または、DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) で微小な遅延を入れる。
ケースC:カスタムトランジションデリゲートで viewWillAppear が呼ばれない
症状
独自のアニメーションを実装したが、遷移先の viewWillAppear が呼ばれない。
原因
animateTransition(using:) 内で toViewController.view を手動で追加しただけで、システムが自動的に viewWillAppear を走らせていない。
解決策
UIViewControllerTransitionCoordinator の animate(alongsideTransition:completion:) を利用し、遷移開始時に viewWillAppear 相当の処理を自前で呼び出す。
Swiftfunc animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let toVC = transitionContext.viewController(forKey: .to) else { return } let container = transitionContext.containerView container.addSubview(toVC.view) toVC.transitionCoordinator?.animate(alongsideTransition: { _ in // ここで viewWillAppear 相当の処理 toVC.viewWillAppear(true) }, completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) }
解決策のまとめ
上記のチェックリストを順に追うことで、ほとんどの「viewWillAppear が呼び出されない」ケースは解決できます。
- シグネチャの正当性 →
override func viewWillAppear(_ animated: Bool)の形に統一。 - 階層と遷移方式の確認 → UINavigationController/UITabBarController の子ビューか、モーダルかを明示。
- Storyboard 設定 → 初期ビューコントローラやインスタンス ID が正しいか確認。
- ログとブレークポイント →
printと Xcode のブレークポイントで実行順序を可視化。 - カスタムトランジション →
transitionCoordinatorを活用し、必要に応じて手動でviewWillAppear相当の処理を注入。
これらを実装すれば、デバッグ時間を大幅に短縮でき、UI のタイミングバグに悩まされることが減ります。
まとめ
本記事では、Swift の viewWillAppear(_:) が期待通りに呼び出されない原因を体系的に整理し、具体的な対処フローとコードサンプルを交えて解説しました。
- シグネチャのミスや
overrideの抜け が最も頻繁に起こる基本ミス - ビュー階層の誤認識(コンテナ vs 子)や Storyboard の設定ミス が実務で多い
- モーダル遷移の連続呼び出しや カスタムトランジション では、遷移完了タイミングを意識した実装が必須
これらのポイントを押さえることで、viewWillAppear が正しく動作し、画面遷移時の状態管理がスムーズになります。次回は、viewWillDisappear と併せた「画面離脱時のリソース解放テクニック」について掘り下げる予定です。
参考資料
- Apple Developer Documentation – UIViewController
- 「iOS アプリ開発入門」 – view controller lifecycle の解説ページ
- Qiita 記事 – Swift で viewWillAppear が呼ばれないケースまとめ
- 『iOS アプリ開発実践入門』 (技術評論社) – 第5章 UIViewController の詳細解説
