UIViewにおけるレイアウトのライフサイクル
以下の3つのステップにより構成される。
- 制約の更新
- フレームの更新
- レンダリング
制約の更新
以下の条件で、Viewの制約が変更されるとUIView
クラスのupdateConstraints()
が呼び出され制約の再計算が行われる
- 制約のactiveフラグによる有効/無効化
- 制約の優先度変更
- 制約の追加・削除
- 制約が与えられたviewの階層の変更
updateConstraints()
内で制約を更新すると、複数のレイアウトパスの中でバッジ処理が行われるため、効率良く制約を更新できる。
制約更新の処理が重い場合などは、updateConstraints()
内で制約を更新したほうが良い。
明示的に制約の更新を行う場合
updateConstraintsIfNeeded()
setNeedsUpdateConstraints()
この二つのメソッドは、制約の更新は行われるがフレームの更新の実行は保証されているわけではない。setNeedsUpdateConstraints()
は即座にupdateConstraints()
は呼ばれず、複数のレイアウトの更新があっても同じレイアウトパスで更新されるためパフォーマンスが良い。
フレームの更新
制約が更新されるとlayoutSubviews()
が呼び出され、フレームが更新される。layoutSubviews()
が呼び出されるとupdateConstraintsIfNeeded()
も呼び出され、必要であれば制約も更新される。
- ビューのフレームが変更された時(端末が回転した時など)
- サブビューの追加・削除
-
UIScrollview
クラスのcontentOffset
が変更された時
明示的にフレームの更新を行う場合
layoutIfNeeded()
setNeedsLayout()
明示的にフレームの更新を行った後、何らかの処理を実行する場合はlayoutIfNeeded()
を使用し、それ以外はsetNeedsLayout()
を使ったほうがパフォーマンスが良い。
layoutSubview()
のオーバライド
super.layoutSubview()
を実行する時点で制約の更新処理は終了しているので、そのレイアウト情報を利用して処理を実行する場合に有効。メインスレッドをブロックせずレイアウトの更新ができる。
トップダウン(親ビュー → 子ビュー)に更新されるので親ビューのレイアウトの更新をすると再起的に更新され、クラッシュを招く。
レンダリング
フレーム情報が更新されるとディスプレイに表示されるためにレンダリングが行われ、drawRect_:
が呼び出される。また、以下の条件で、drawRect_:
が呼び出され再描画を行う。
- ビューの一部を隠してる別のビューの移動・削除
- hidden(非表示)になっていたビューの再表示
- ビューを画面外までスクロールし、再び画面内に戻す
- ビューの
setNeedsDisplay()
,setNeedsDisplayInRect(_:)
の明示的な呼び出し