Auto Layout 原理
Auto Layout是一種全新的布局方式,它采用一系列約束(constraints)來實現(xiàn)自動布局,當你的屏幕尺寸發(fā)生變化或者屏幕發(fā)生旋轉(zhuǎn)時,可以不用添加代碼來保持原有布局不變,實現(xiàn)視圖的自動布局。
所謂約束,通常是定義了兩個視圖之間的關(guān)系(當然你也可以一個視圖自己跟自己設(shè)定約束)。如下圖就是一個約束的例子,當然要確定一個視圖的位置,跟基于frame一樣,也是需要確定視圖的橫縱坐標以及寬度和高度的,只是,這個橫縱坐標和寬度高度不再是寫死的數(shù)值,而是根據(jù)約束計算得來,從而達到自動布局的效果。
UIView之drawRect: & layoutSubviews的作用和機制
drawRect 調(diào)用機制
1、調(diào)用時機:loadView ->ViewDidload ->drawRect:
2、如果在UIView初始化時沒有設(shè)置rect大小,將直接導致drawRect:不被自動調(diào)用。
3、通過設(shè)置contentMode屬性值為UIViewContentModeRedraw。那么將在每次設(shè)置或更改frame的時候自動調(diào)用drawRect:。
4、直接調(diào)用setNeedsDisplay,或者setNeedsDisplayInRect:觸發(fā)drawRect:,但是有個前提條件是:view當前的rect不能為nil
5、該方法在調(diào)用sizeThatFits后被調(diào)用,所以可以先調(diào)用sizeToFit計算出size。然后系統(tǒng)自動調(diào)用drawRect:方法。
這里簡單說一下sizeToFit和sizeThatFit:
sizeToFit:會計算出最優(yōu)的 size 而且會改變自己的size
sizeThatFits:會計算出最優(yōu)的 size 但是不會改變 自己的 size
注意事項
1、若使用UIView繪圖,只能在drawRect:方法中獲取相應(yīng)的contextRef并繪圖。如果在其他方法中獲取到一個invalidate的ref保存下來,在drawRect中并不能用于畫圖。等到在這里調(diào)用時,可能當前上下文環(huán)境已經(jīng)變化。
2、若使用CALayer繪圖,只能在drawInContext: 中(類似于drawRect)繪制,或者在delegate中的相應(yīng)方法繪制。同樣也是調(diào)用setNeedDisplay等間接調(diào)用以上方法。
3、若要實時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來掉用setNeedsDisplay實時刷新屏幕。
4、UIImageView繼承自UIView,但是UIImageView能不重寫drawRect方法用于實現(xiàn)自定義繪圖。具體原因如下:
Apple在文檔中指出:UIImageView是專門為顯示圖片做的控件,用了最優(yōu)顯示技術(shù),是不讓調(diào)用darwrect方法, 要調(diào)用這個方法,只能從uiview里重寫。
layoutSubviews
這個方法是用來對subviews重新布局,默認沒有做任何事情,需要子類進行重寫。
當我們在某個類的內(nèi)部調(diào)整子視圖位置時,需要調(diào)用。
反過來的意思就是說:如果你想要在外部設(shè)置subviews的位置,就不要重寫。
①、- (void)layoutSubviews;
對subview重新布局
②、- (void)setNeedsLayout;
將視圖標記為需要重新布局, 這個方法會在系統(tǒng)runloop的下一個周期自動調(diào)用layoutSubviews。
③、- (void)layoutIfNeeded;
如果有需要刷新的標記,立即調(diào)用layoutSubviews進行布局(如果沒有標記,不會調(diào)用layoutSubviews)這里注意一個點:標記,沒有標記,即使我們掉了該函數(shù)也不起作用。
如果要立即刷新,要先調(diào)用[view setNeedsLayout],把標記設(shè)為需要布局,然后馬上調(diào)用[view layoutIfNeeded],實現(xiàn)布局.
在視圖第一次顯示之前,標記總是“需要刷新”的,可以直接調(diào)用[view layoutIfNeeded]
這里有必要描述下三者之間的關(guān)系:
在沒有外界干預的情況下,一個view的frame或者bounds發(fā)生變化時,系統(tǒng)會先去標記flag這個view,等下一次渲染時機到來時(也就是runloop的下一次循環(huán)),會去按照最新的布局去重新布局視圖。
setNeedLayout就是給這個view添加一個標記,告訴系統(tǒng)下一次渲染時機需要重新布局這個視圖。
layoutIfNeed就是告訴系統(tǒng),如果已經(jīng)設(shè)置了flag,那不用等待下個渲染時機到來,立即重新渲染。前提是設(shè)置了flag。
而layoutSubviews則是由系統(tǒng)去調(diào)用,不需要我們主動調(diào)用,我們只需要調(diào)用layoutIfNeed,告訴系統(tǒng)是否立即執(zhí)行重新布局的操作。
layoutSubviews調(diào)用時機
結(jié)論是經(jīng)過搜索得到的,基于此筆者進行了驗證,并得到了些結(jié)果:
1、init初始化不會觸發(fā)layoutSubviews。
2、addSubview會觸發(fā)layoutSubviews。(當然這里frame為0,是不會調(diào)用的,同上面的drawrect:一樣)
3、設(shè)置view的Frame會觸發(fā)layoutSubviews,(當然前提是frame的值設(shè)置前后發(fā)生了變化。)
4、滾動一個UIScrollView會觸發(fā)layoutSubviews。
5、旋轉(zhuǎn)屏幕會觸發(fā)父UIView上的layoutSubviews事件。(這個我們開發(fā)中會經(jīng)常遇到,比如屏幕旋轉(zhuǎn)時,為了界面美觀我們需要修改子view的frame,那就會在layoutSubview中做相應(yīng)的操作)
6、改變一個UIView大小的時候也會觸發(fā)父UIView上的layoutSubviews事件。
7、直接調(diào)用setLayoutSubviews。(Apple是不建議這么做的)
這里需要補充一點:
layoutSubview是布局相關(guān),而drawRect則是負責繪制。因此從調(diào)用時序上來講,layoutSubviews要早于drawRect:函數(shù)。