layoutSubviews 這個方法,默認沒有做任何事情,需要子類進行重寫。
(This method) Lays out subviews.
Subclasses can override this method as needed to perform more precise layout of their subviews. You should override this method only if the autoresizing and constraint-based behaviors of the subviews DO NOT offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
以上節(jié)選自 UIView Class Reference
Whenever the size of a view changes, UIKit applies the autoresizing behaviors of that view’s subviews and THEN calls the layoutSubviews method of the view to let it make manual changes. You can implement the layoutSubviews method in custom views when the autoresizing behaviors by themselves DO NOT yield the results you want. Your implementation of this method can do any of the following:
Adjust the size and position of any immediate subviews.
Add or remove subviews or Core Animation layers.
Force a subview to be redrawn by calling its setNeedsDisplay or setNeedsDisplayInRect: method.
One place where applications often lay out subviews manually is when implementing a large scrollable area. Because it is impractical to have a single large view for its scrollable content, applications often implement a root view that contains a number of smaller tile views. Each tile represents a portion of the scrollable content. When a scroll event happens, the root view calls its setNeedsLayout method to initiate a layout change. Its layoutSubviews method then repositions the tile views based on the amount of scrolling that occurred. As tiles scroll out of the view’s visible area, the layoutSubviews method moves the tiles to the incoming edge, replacing their contents in the process.
以上節(jié)選自 View Programming Guide for iOS
根據(jù)官方文檔的描述,可以知道 layoutSubviews 主要是為了讓我們?nèi)崿F(xiàn)UIView的策略來布局以及排列等,這樣就可以保證我們在需要新的布局方案的時候,父類UIView 會按照我們制定的方案去布局。我們在某個類的內(nèi)部調(diào)整子視圖位置時,需要調(diào)用。反過來的意思就是說:如果你想要在外部設(shè)置 sub views 的位置,就不要重寫。而且,layoutSubviews 方法只能被系統(tǒng)觸發(fā)調(diào)用,不可以手動去調(diào)用。 要引起該方法的調(diào)用,可以調(diào)用 UIView 的 setNeedsLayout 方法來標(biāo)記一個 UIView。這樣一來,在 UI 線程的下次繪制循環(huán)中,系統(tǒng)便會調(diào)用該 UIView 的 layoutSubviews 方法。
在蘋果的官方文檔中強調(diào):
You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.
layoutSubviews, 當(dāng)我們在某個類的內(nèi)部調(diào)整子視圖位置時,需要調(diào)用;如果你想要在外部設(shè)置subviews的位置,就不要重寫。
layoutSubviews 默認沒有做任何事情,既然我不能手動直接調(diào)用該方法,那在什么時候、何種條件下這個方法會被調(diào)用呢?
Stackoverflow 上已經(jīng)有相關(guān)的討論了(作者在他的博客上有更詳細的描述),并且有一位朋友給出了很不錯的解答:
- init does not cause layoutSubviews to be called (duh)
- addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target
- view setFrame intelligently calls layoutSubviews on the view having its frame set only if the size parameter of the frame is different
- scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and its superview
- rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
- Resizing a view will call layoutSubviews on its superview
翻譯:
- init初始化不會觸發(fā)layoutSubviews 但是是用initWithFrame 進行初始化時,當(dāng)rect的值不為CGRectZero時,也會觸發(fā)
- addSubview會觸發(fā)layoutSubviews
- 設(shè)置view的Frame會觸發(fā)layoutSubviews,當(dāng)然前提是frame的值設(shè)置前后發(fā)生了變化
- 滾動一個UIScrollView會觸發(fā)layoutSubviews
- 旋轉(zhuǎn)Screen會觸發(fā)父UIView上的layoutSubviews事件
- 改變一個UIView大小的時候也會觸發(fā)父UIView上的layoutSubviews事件
iOS 中 layout 的相關(guān)方法:
- layoutSubviews
- layoutIfNeeded
- setNeedsLayout
- setNeedsDisplay
- drawRect
- sizeThatFits
- sizeToFit
當(dāng)然這并不齊全。
1. setNeedsLayout方法:
標(biāo)記為需要重新布局,異步調(diào)用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定會被調(diào)用
2. layoutIfNeeded方法:
如果有需要刷新的標(biāo)記,立即調(diào)用layoutSubviews進行布局;如果沒有標(biāo)記,不會調(diào)用layoutSubviews;如果要立即刷新,要先調(diào)用[view setNeedsLayout],把標(biāo)記設(shè)為需要布局,然后馬上調(diào)用[view layoutIfNeeded],實現(xiàn)布局。在視圖第一次顯示之前,標(biāo)記總是“需要刷新”的,可以直接調(diào)用[view layoutIfNeeded]
3. 重繪
drawRect方法:重寫此方法,執(zhí)行重繪任務(wù)
setNeedsDisplay方法:標(biāo)記為需要重繪,異步調(diào)用drawRect
setNeedsDisplayInRect方法:標(biāo)記為需要局部重繪
4. sizeToFit
sizeToFit會自動調(diào)用sizeThatFits方法;
sizeToFit不應(yīng)該在子類中被重寫,應(yīng)該重寫sizeThatFits
sizeThatFits傳入的參數(shù)是receiver當(dāng)前的size,返回一個適合的size
sizeToFit可以被手動直接調(diào)用
sizeToFit和sizeThatFits方法都沒有遞歸,對subviews也不負責(zé),只負責(zé)自己
Tips:
layoutSubviews對subviews重新布局
layoutSubviews方法調(diào)用先于drawRect
setNeedsLayout在receiver標(biāo)上一個需要被重新布局的標(biāo)記,在系統(tǒng)runloop的下一個周期自動調(diào)用layoutSubviews
layoutIfNeeded方法如其名,UIKit會判斷該receiver是否需要layout.根據(jù)Apple官方文檔,layoutIfNeeded方法應(yīng)該是這樣的
layoutIfNeeded遍歷的不是superview鏈,應(yīng)該是subviews鏈
drawRect是對receiver的重繪,能獲得context
setNeedDisplay在receiver標(biāo)上一個需要被重新繪圖的標(biāo)記,在下一個draw周期自動重繪,iphone device的刷新頻率是60hz,也就是1/60秒后重繪