UIViewControllerのライフサイクル

  • 376
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

UIViewControllerのライフサイクルについて

UIViewControllerのライフサイクルについてまとめてみました。いざまとめてみるとviewの読み込みや表示に関する部分はある程度理解していたものの、レイアウト関連の処理については理解が甘かったことを認識しました。まとめてみたものの、あまり自信がありません(引用部分は基本的にリファレンスから引っ張ってきています)。

ライフサイクル

アクティビティ図

ざっくりとまとめると、だいたいこんな感じ。

ViewControllerのライフサイクル.png

Autolayout使用時に結構つまずいたのがviewWillLayoutSubviewsviewDidLayoutSubviewsなので、レイアウトはこの辺でやってるんだ〜くらいを押さえられれば良いと思います。

loadView, viewDidLoad

読み込み系ライフサイクルメソッド(と勝手に名付けた)。

loadView

nib=xibなどを使用しない場合にカスタムViewの初期化を行う箇所。

- (void)loadView; // This is where subclasses should create their custom view hierarchy if they aren't using a nib. Should never be called directly.

Storyboardやxibなど、InterfaceBuilderを利用している場合はこのメソッドを呼び出してはならない。

If you use Interface Builder to create your views and initialize the view controller, you must not override this method.

viewDidLoad

ViewControllerのviewがロードされた後に呼び出される。
上記の通り、InterfaceBuilderを使用している場合はサブビューのセットアップは一般的にここで行うことになる。

- (void)viewDidLoad; // Called after the view has been loaded. For view controllers created in code, this is after -loadView. For view controllers unarchived from a nib, this is after the view is set.

viewWill~, viewDid~系メソッド

viewWill~viewDid~ともに3種類存在する。

viewWillAppear:, viewDidAppear:

表示系ライフサイクルメソッド(と勝手に名付けた)。

- (void)viewWillAppear:(BOOL)animated;    // Called when the view is about to made visible. Default does nothing
- (void)viewDidAppear:(BOOL)animated;     // Called when the view has been fully transitioned onto the screen. Default does nothing

viewWillAppear:はviewが表示される直前に呼ばれる。
viewDidAppear:は完全に遷移が行われ、スクリーン上に表示された時に呼ばれる。

注意点としてはviewDidAppear:が呼ばれたタイミングではすでに画面に描画されている状態なので、このタイミングでviewの生成などを行うと当然おかしなことになる。

viewWillDisappear:, viewDidDisappear:

非表示系ライフサイクルメソッド(とこちらも勝手に名付けた)。
表示系ライフルメソッドと対になるもの。

- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated;  // Called after the view was dismissed, covered or otherwise hidden. Default does nothing

viewWillDisappear:はviewが表示されなくなる直前に呼び出される。
viewDidDisappear:は完全に遷移が行われ、スクリーン上からViewControllerが表示されなくなったときに呼ばれる。

注意点としては、viewDidDisappear:が呼び出されたからといってviewControllerオブジェクトが破棄されるわけではないということ。

例えばUITabBarControllerUINavigationControllerなどに保持され続けている場合はviewControllerは保持される。

viewWillLayoutSubviews, viewDidLayoutSubviews

レイアウト系ライフサイクルメソッド(勝手に名付け(ry

これらの動きを理解するにはUIViewのレイアウト処理について理解しておかなければならないので、一旦そちらをまとめる。

UIViewのレイアウトについて

たくさんあるけど、ひとまず以下の2つにしぼります。

- (void)updateConstraints NS_AVAILABLE_IOS(6_0); // Override this to adjust your special constraints during a constraints update pass

- (void)layoutSubviews;    // override point. called by layoutIfNeeded automatically. As of iOS 6.0, when constraints-based layout is used the base implementation applies the constraints-based layout, otherwise it does nothing.

updateConstraintsは制約の更新のために呼ばれ、layoutSubviewsはレイアウト処理のために呼ばれる。カスタムの制約を付けたい、あるいは削除したい場合はupdateConstraintsをオーバーライドする。

順番としては「制約の更新→レイアウト更新→描画」となるのでupdateConstraintslayoutSubviewsの順番で呼ばれる。

layoutSubviewssetNeedsLayoutあるいはlayoutIfNeededを使用して呼び出すこと。直ちに呼び出したい場合はlayoutIfNeededを呼び出すこと(リファレンスを呼んだ限りupdateConstraintsを直接呼び出してはいけないという記述はなかったが、updateConstraintsIfNeededsetNeedsUpdateConstraintsというメソッドが用意されているので使った方が無難そう)。

UIViewControllerのレイアウト系メソッドについて

ViewControllerの話に戻り、UIViewのレイアウト系メソッドを踏まえた上でこちらを読んでみる。

1.View Controllerのビューが新しい大きさに変わります。
2.自動レイアウト処理が無効であれば、自動大きさ調整マスクに従ってビューの大きさが変わります。
3.View ControllerのviewWillLayoutSubviewsメソッドが呼び出されます。
4.ビューのlayoutSubviewsメソッドが呼び出されます。ビュー階層を構築するために自動レイアウト機能が使われていれば、次の手順でレイアウト制約を更新できます。

a. View ControllerのupdateViewConstraintsメソッドが呼び出されます。
b. UIViewControllerクラスのupdateViewConstraintsメソッドは、処理の過程でビューのupdateConstraintsメソッドを呼び出します。
c. レイアウト制約の更新後、新しいレイアウトを計算し、ビューの位置を調整し直します。

5.View ControllerのviewDidLayoutSubviewsメソッドが呼び出されます。

引用:https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerPGforiOS.pdf

viewWillLayoutSubviewsviewControllerviewlayoutSubviewsメソッドが実行されるに呼ばれる。

updateViewConstraints

viewDidLayoutSubviewsviewControllerviewlayoutSubviewsメソッドが実行されたに呼ばれる。

// Called just before the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a nop.
- (void)viewWillLayoutSubviews NS_AVAILABLE_IOS(5_0);
// Called just after the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a nop.
- (void)viewDidLayoutSubviews NS_AVAILABLE_IOS(5_0);

/* Base implementation sends -updateConstraints to the view.
When a view has a view controller, this message is sent to the view controller during
the autolayout updateConstraints pass in lieu of sending updateConstraints directly
to the view.
You may override this method in a UIViewController subclass for updating custom
constraints instead of subclassing your view and overriding -[UIView updateConstraints].
Overrides must call super or send -updateConstraints to the view.
*/
- (void)updateViewConstraints NS_AVAILABLE_IOS(6_0);

dealloc

これはUIViewControllerのメソッドではなくオブジェクトが解放されるときに呼ばれるメソッド。

典型的な使い道としてはNSNotificationCenterを利用した通知登録解除、などを行う。delegateの解除などを行う。

-(void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:HOGE_KEY
                                                  object:nil];

    self.tableView.delegate = nil;
}

遷移の構造によってはviewDidDisappear:が呼ばれたからといってオブジェクトが破棄されるわけではないので、上記のようなdelegateの破棄や通知センターの解除を行ってしまうと困る可能性がある。

例えばTabBarControllerのような場合は、画面に表示されなくなってもそこに追加されているViewControllerオブジェクトは破棄されないままなので注意。

残りがちなDeprecatedメソッド

viewWillUnloadviewDidUnloadはもうなくなっているので、アプリがサポートしていないOSのソースに残っている場合は消してしまいましょう。

- (void)viewWillUnload NS_DEPRECATED_IOS(5_0,6_0);
- (void)viewDidUnload NS_DEPRECATED_IOS(3_0,6_0); // Called after the view controller's view is released and set to nil. For example, a memory warning which causes the view to be purged. Not invoked as a result of -dealloc.

まとめ

タイミングを間違えるとバグの温床になるので、ViewControllerのタイミングについてはしっかりと押さえたいところです。

ただし、冒頭でも書きましたが、viewWillLayoutSubviewsviewDidLayoutSubviewsなどのレイアウト周りはあまり自信がないので間違いがあれば指摘していただけると助かります。

今回は省略していますが、UIViewwillMoveToSuperview:didMoveToSuperviewなどのライフサイクルについても覚えておくとそのうち役に立つと思います。