天天看點

UIView相關Updating constraints、Layout、Display一些知識

一、首先介紹幾組api

1)基于觸發限制的布局 Triggering Constraint-Based Layout

constraint-based layout system使用此傳回值去決定是否需要 調用updateConstraints作為正常布局過程的一部分。

- (void)setNeedsUpdateConstraints
           

當一個自定義view的某個屬性發生改變,并且可能影響到constraint時,需要調用此方法去标記constraints需要在未來的某個點更新,系統然後調用updateConstraints.

- (void)updateConstraints
           

更新限制,自定義view應該重寫此方法在其中建立constraints. 注意:要在實作在最後調用[super updateConstraints]

- (void)updateConstraintsIfNeeded
           

立即觸發限制更新,自動更新布局。

2)鋪設子視圖 Laying out Subviews

如果你需要更精确控制子view,而不是使用限制或autoresizing行為,就需要實作該方法。

使用此方法強制立即進行layout,從目前view開始,此方法會周遊整個view層次(包括superviews)請求layout。是以,調用此方法會強制整個view層次布局。

- (void)setNeedsLayout
           

此方法會将view目前的layout設定為無效的,并在下一個upadte cycle裡去觸發layout更新。

3)繪畫和更新視圖 Drawing and Updati

如果你的View畫自定義的内容,就要實作該方法,否則避免覆寫該方法。

-  void)setNeedsDisplay
           

标記整個視圖的邊界矩形需要重繪

标記在指定區域内的視圖的邊界需要重繪

相關聯的改變 Observing View-Related Changes

通知視圖指定子視圖已經添加

通知視圖将要移除指定的子視圖

通知視圖将要移動到一個新的父視圖中

通知視圖已經移動到一個新的父視圖中

通知視圖将要移動到一個新的window中

通知視圖已經移動到一個新的window中

5)配置自動調整大小狀态 Configuring the Resizing Behavior

根據子視圖的大小位置,調整視圖,使其恰好圍繞子視圖, 也就是說自動适應子視圖的大小,隻顯示子視圖

讓視圖計算最适合子視圖的大小,即能把全部子視圖顯示出來所需要的最小的size

二、Auto Layout Process 自動布局過程

1)

布局過程

布局過程與使用springs and struts(autoresizingMask)比較,Auto layout在view顯示之前,多引入了兩個步驟:updating constraints 和laying out views。每一個步驟都依賴于上一個。display依賴layout,而layout依賴updating constraints。 updating constraints->layout->display

第一步:

updating constraints,被稱為測量階段,其從下向上(from subview to super view),為下一步layout準備資訊。可以通過調用方法setNeedUpdateConstraints去觸發此步。constraints的改變也會自動的觸發此步。但是,當你自定義view的時候,如果一些改變可能會影響到布局的時候,通常需要自己去通知Auto layout,updateConstraintsIfNeeded。

自定義view的話,通常可以重寫updateConstraints方法,在其中可以添加view需要的局部的contraints。

第二步:

layout,其從上向下(from super view to subview),此步主要應用上一步的資訊去設定view的center和bounds。可以通過調用setNeedsLayout去觸發此步驟,此方法不會立即應用layout。如果想要系統立即的更新layout,可以調用layoutIfNeeded。另外,自定義view可以重寫方法layoutSubViews來在layout的工程中得到更多的定制化效果。

第三步:

display,此步時把view渲染到螢幕上,它與你是否使用Auto layout無關,其操作是從上向下(from super view to subview),通過調用setNeedsDisplay觸發,setNeedsDisplay觸發系統會調用UIView 的 drawRect方法。

因為每一步都依賴前一步,是以一個display可能會觸發layout,當有任何layout沒有被處理的時候,同理,layout可能會觸發updating constraints,當constraint system更新改變的時候。

需要注意的是,這三步不是單向的,constraint-based layout是一個疊代的過程,layout過程中,可能去改變constraints,又一次觸發updating constraints,進行一輪layout過程。

下面說說UIViewController的布局過程

VC的生命周期的部分過程

viewDidLoad -> viewWillAppear -> updateViewConstraints -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisAppear -> updateViewConstraints -> viewDidDisAppear

對應updateConstraints -> layoutSubViews -> drawRect

當view修改限制(addConstraint, removeConstraint)會觸發setNeedsUpdateConstraints,而這個在layoutSubViews之前會觸發updateConstraints,完成之後會調用layoutSubViews。UIViewController在有個updateViewConstraints 方法,這個方法實際是self.view 被設定了setNeedsUpdateConstraints(第一次展示的時候),必然會調用這個方法(與上面的解釋保持一緻了,第一次可以了解為為self.view增加了各種限制)。而這個方法的預設實作是調用子view的updateConstraints方法,這樣就自上而下的完成了布局。

此處需要注意的地方:

1. 不要忘記調用父類的方法,避免有時候出現一些莫名的問題。

2. 在view的layoutSubViews或者ViewController的viewDidLayoutSubviews方法裡後可以拿到view的實際frame,是以當我們真的需要frame的時候需要在這個時間點以後才能拿到。

下面我們可以解釋是為什麼viewDidLoad裡通過setFrame的方式改過原先在storyboard裡拖動的限制代碼無效了。因為updateViewConstraints在viewDidLoad後執行,會覆寫掉之前的設定的frame,是以無效。

三、補充

layoutSubviews在以下情況下會被調用:

1、init初始化不會觸發layoutSubviews

但是是用initWithFrame 進行初始化時,當rect的值不為CGRectZero時,也會觸發

2、addSubview會觸發layoutSubviews

3、設定view的Frame會觸發layoutSubviews,當然前提是frame的值設定前後發生了變化

4、滾動一個UIScrollView會觸發layoutSubviews

5、旋轉Screen會觸發父UIView上的layoutSubviews事件

6、改變一個UIView大小的時候也會觸發父UIView上的layoutSubviews事件

重新整理子對象布局

-layoutSubviews方法:這個方法,預設沒有做任何事情,需要子類進行重寫

-setNeedsLayout方法: 标記為需要重新布局,異步調用layoutIfNeeded重新整理布局,不立即重新整理,但layoutSubviews一定會被調用

-layoutIfNeeded方法:如果,有需要重新整理的标記,立即調用layoutSubviews進行布局(如果沒有标記,不會調用layoutSubviews)

如果要立即重新整理,要先調用[view setNeedsLayout],把标記設為需要布局,然後馬上調用[view layoutIfNeeded],實作布局

在視圖第一次顯示之前,标記總是“需要重新整理”的,可以直接調用[view layoutIfNeeded]

重繪

-drawRect:(CGRect)rect方法:重寫此方法,執行重繪任務

-setNeedsDisplay方法:标記為需要重繪,異步調用drawRect

-setNeedsDisplayInRect:(CGRect)invalidRect方法:标記為需要局部重繪

sizeToFit會自動調用sizeThatFits方法;

sizeToFit不應該在子類中被重寫,應該重寫sizeThatFits

sizeThatFits傳入的參數是receiver目前的size,傳回一個适合的size

sizeToFit可以被手動直接調用

sizeToFit和sizeThatFits方法都沒有遞歸,對subviews也不負責,隻負責自己

layoutSubviews對subviews重新布局

layoutSubviews方法調用先于drawRect

setNeedsLayout在receiver标上一個需要被重新布局的标記,在系統runloop的下一個周期自動調用layoutSubviews

layoutIfNeeded方法如其名,UIKit會判斷該receiver是否需要layout.根據Apple官方文檔,layoutIfNeeded方法應該是這樣的

layoutIfNeeded周遊的不是superview鍊,應該是subviews鍊

drawRect是對receiver的重繪,能獲得context。

以上這些進一步驗證了上面的說法。

總之,了解view布局的過程,可以幫助你了解View顯示的相關問題,解決一些界面問題,合理使用以上方法對你自定義控件也有很大的幫助。