UIView Auto Layout life cycle
Steps overview
- 當開啟autolayout的UIView,都會經歷下列生命週期。
- init, update, layout, render。
- 不絕對是單一方向,任何step都可以觸發先前的step,甚至可以強制重新整個life cycle。
Update step
- 系統會由上而下去呼叫每個subview的updateConstraints()。
- 基本上,這個step是自動被執行的,但有時候會希望可以手動觸發這個step。
- 例如,當使用者改變app某個狀態時,會希望畫面產生變化,這時候就要手動觸發這個step。
- 手動觸發的做法
- setNeedsUpdateConstraints,會讓目前constraints無效,然後在下一個cycle更新constraints。
- updateConstraintsIfNeeded,會觸發updateConstraints()這個method。
[cell.contentView setNeedsUpdateConstraints];
[cell.contentView updateConstraints];
Layout step
- 系統會由下而上去呼叫superview的layoutSubviews。
- layoutSubviews是整個life cycle中最常見到被overridden的方法。
- 當constraints無法滿足去呈現一個畫面時。
- 手動計算frames。
基本上,layoutSubviews也可以手動被觸發。
- setNeedsLayout,會讓目前view’s layout無效,然後在下一個cycle更新layout。
- layoutIfNeeded,會觸發layoutSubviews()這個method。
[self setNeedsLayout]; [self layoutIfNeeded];
- 在overridden layoutSubviews時,需要特別注意下列事項。
- 別忘記執行super.layoutSubviews()。
- 千萬別呼叫setNeedsLayout,setNeedsUpdateConstraints,不然會造成無限迴圈。
- 目前這個view所在hierarchy以外的其他view,不要去改動其constraints。
- 如果要改動這個view所在hierarchy的其他view(parent or child),千萬要小心,有可能觸發其他view的update,而導致無限迴圈。
Rendering
- UIView會將目前需要呈現pixel的工作交給CALayer進行處理。
- Rendering step是獨立,不管是否有開啟auto layout。
- 除非你是在進行一些客製化的繪製OpenGL ES, Core Graphics or UIKit drawing,不然通常不會去overridden drawRect。
What about UIViewControllers
- 在vc的生命週期中,也有相對應的method,讓vc知道目前畫面在constraint update或view Layout是否已經完成。
- Update phase: updateViewConstraints
- Layout phase: viewWillLayoutSubviews / viewDidLayoutSubviews
- 其中viewDidLayoutSubviews是最重要,也最常被overridden。
- 通知vc目前所有view已經完成layout step了。
- 這也是最好時機,進行畫面的最後更新,例如改顏色,改frame等。
- 過了viewDidLayoutSubviews,最後的畫面就會呈現給使用者。
Intrinsic Content Size
- 透過overridden intrinsicContentSize,來回傳適合目前content的view大小。
- (CGSize)intrinsicContentSize
{
CGSize size = [super intrinsicContentSize];
size.width += self.edgeInsets.left + self.edgeInsets.right;
size.height += self.edgeInsets.top + self.edgeInsets.bottom;
return size;
}
- If a view has an intrinsic size only for one dimension, you should still override intrinsicContentSize and return UIViewNoIntrinsicMetric for the unknown dimension
Alignment Rectangle
- alignmentRect(forFrame:), frame(forAlignmentRect:) 实现这些方法来重写视图与其他视图的对齐方式。
- 同樣要將兩個圓形帶有陰影的image進行置中,會發現左邊沒辦法置中。
- 因為左邊的image的陰影是放在image裡面,而右邊的image的陰影是另外用程式來產生的。
- The key to understanding their positioning is the difference in alignment rectangles. Since shadow is a part of the left view’s image, it is also a part of its alignment rectangle. Thus, the center of alignment rectangle does not match with the circle center.