今回はUIViewの表示についてまとめようと思います。
AutoLayoutが効かない・Viewの表示位置がおかしい・表示されない・画面表示までが遅いことが実装している中で1度は経験はあると思います。
このような問題は、Viewの表示の流れを理解せずに開発していることが原因かもしれません。
UIViewのレイアウトライフサイクルは下記の順で行われます。
- 制約の更新
- フレームの更新
- レンダリング
それでは、1つずつ見ていきましょう!
制約の更新
以下の条件で、Viewの制約が変更されるとUIViewクラスのupdateConstraints()
が呼び出され制約の再計算が行われます。
- 制約のactiveフラグの有効・無効の切り替え
- 制約の優先度の変更
- 制約の追加・削除
- 制約が与えられたviewの階層の変更
updateConstraints()はシステム側がレイアウトパフォーマンスを考慮したタイミングで呼び出すので、意図的に呼び出してはいけません。
ただし、パフォーマンスを大きく損なうような制約の更新などの場合は updateConstraint() 内で行なった方がいいです。
これは、updateConstraint() がレイアウトののバッチ処理を行いメインスレッドをブロックしないためです。
使い方としては、UIViewのサブクラスでメソッドをoverrideして使用します。
意図的に制約の計算を行いたい場合には、下記のメソッドを使いましょう。
- updateConstraintsIfNeeded()
- setNeedsUpdateConstraints()
2つの違いはsetNeedsUpdateConstraints() は指定された ViewとそのSubviews がシステムのタイミングによって updateConstraint() を呼ぶようになり、updateConstraintsIfNeeded() は更新を即座に行います。
ただ、この2つのメソッドは、制約の更新は行われるが次に説明するフレームの更新の実行は保証されているわけではないです。
フレームの更新
制約が更新されるとlayoutSubviews()
が呼び出され、フレームが更新されます。
フレームの更新というのはあるViewのサイズに合わせてSubViewやlayerの配置を更新することです。
layoutSubviews()が呼び出されるとupdateConstraintsIfNeeded()
も呼び出され、必要であれば制約も更新されます。
ただし、制約の更新と同じようにlayoutSubviews()は意図的に呼び出してはいけません。
意図的に更新したい場合には、下記のメソッドを使います。
- setNeedsLayout()
- layoutIfNeeded()
2つの違いはsetNeedsLayout()
は 次の再描画が行われる際に更新されるのに対し、layoutIfNeeded()
は更新を即座に行います。
setNeedsLayout()は更新リクエストのフラグをONにするイメージです。
なので、明示的にフレームの更新を行った後に何らかの処理を実行する場合はlayoutIfNeeded()を使用し、それ以外はsetNeedsLayout()を使ったほうがパフォーマンスが良いと言われています。
レンダリング
フレーム情報が更新されると画面に表示されるためにレンダリングが行われ、draw(rect:)
が呼び出されます。また、以下の条件で、draw(rect:)が呼び出され再描画を行います。
- Viewの追加・削除
- 非表示になっていたViewの再表示
- Viewを画面外までスクロールし、再び画面内に戻す
- ViewのsetNeedsDisplay(), setNeedsDisplayInRect(_:)の明示的な呼び出し
ここまでUIViewが描画されるまでの流れを書いていきました。もう少し細かいところまで調べて追記していきたいと思います。