1 iOS常用的布局方式
iOS中界面有三種布局方式:Frame,Autoresizing Masks和Auto Layout沃暗。
一般而言Frame是最隨心所欲的魁兼,你可以做你任何想要的改變坐搔,但是同時也是最繁瑣的藏研,一旦布局發(fā)生改變你要更改所有的相關視圖的Frame。Autoresizing Masks定義了父視圖frame變化時子視圖的frame如何變化概行,這簡化了視圖外部布局變化時內部視圖變化的工作量蠢挡。Auto Layout使用約束來表示視圖間的相應關系,不論是外部變化還是內部變化凳忙,視圖都可以動態(tài)相應业踏,大大簡化了我們的布局方式。
以上是三種布局方式的主要特點涧卵,但也不絕對勤家,比如使用Frame的時候我們也會根據(jù)其他視圖的left,right去編寫布局柳恐,這樣也有了視圖間的相互關系的意味伐脖。使用Auto Layout時的leading,trailing很多時候都是一個常量并不符合我們的要求乐设,還要在視圖加載或者變化過程中去修改值才能達到動態(tài)響應變化的效果讼庇。所以使用哪種布局方式好并沒有一個定論,而且我也支持在特定的情況下使用有優(yōu)勢的布局方式近尚。這樣就造成了對于生命力較長的項目會出現(xiàn)多種方式并存的局面蠕啄,本文章主要講對于Frame和Auto Layout混用情況下的一些避坑策略。
2 視圖加載過程
要混用Frame和Auto Layout肿男,必須要搞清楚他們的作用時間和有效范圍介汹,這和視圖的加載過程有很大的關系。我們在controller視圖上面用auto layout添加了一個subview舶沛,并在各個系統(tǒng)調用聲明周期方法中做了打印處理嘹承,結果如下:
viewDidLoad
viewWillAppear
viewWillLayoutSubviews(1)
-updateConstraints
updateViewConstraints
viewWillLayoutSubviews(2)
viewDidLayoutSubviews
-layoutSubviews
-drawRect
viewDidAppear
其中前面帶有“-”符號的表示的是subview視圖的打印。對于viewWillLayoutSubviews出現(xiàn)的順序根據(jù)使用的布局方式會有所區(qū)別如庭,使用Frame時出現(xiàn)為viewWillLayoutSubviews(1)位置叹卷,使用Auto Layout時出現(xiàn)在viewWillLayoutSubviews(2)位置。
Auto Layout的作用范圍是從第三個方法-updateConstraints開始直到viewDidLayoutSubviews完成坪它,在這期間系統(tǒng)是通過view上的約束來計算view上的布局骤竹。一般我們都是在viewDidLoad里面編寫頁面布局代碼,如果這時對一個視圖同時使用了Auto Layout和Frame往毡,我們會發(fā)現(xiàn)frame無效蒙揣,就是因為這時布局是按照約束來計算的。如果視圖的frame在布局過程中發(fā)生了改變(比如initWithFrame开瞭,使用xib都會給予初始值懒震,但實際布局時我們可能想要更改)罩息,這時如果想獲取視圖的準確frame值,在viewWillAppear中是不行的个扰,只能在自動布局生效后獲取到瓷炮,即我們可以再viewDidLayoutSubviews和viewDidAppear中獲取frame的準確值。由上述結果也可以得出递宅,系統(tǒng)的布局是優(yōu)先使用Auto Layout的娘香,但是布局的最終結果卻是將約束轉化成視圖的frame,理解了這一點對于布局方式的選用也很重要办龄。
要想修改布局烘绽,必須要在Auto Layout結束之后才會起作用,否則會被系統(tǒng)將我們的布局按照Auto Layout重新刷新土榴。從上面我們可以看到诀姚,我們可以在子視圖的-layoutSubviews和-drawRect方法里面修改子視圖的布局,但是一般視圖為了重用不會在自己的類里面將frame寫死玷禽,這樣我們只有通過controller的viewDidLayoutSubviews和viewDidAppear方法修改視圖的frame。但是這樣也有問題呀打,在viewDidAppear里面修改布局矢赁,我們可以看到一個明顯的延遲,系統(tǒng)調用次序使然贬丛,我們無法去改變撩银。
所以我的建議就是不要對同一個視圖同時使用Frame和Auto Layout去控制同一個視圖。如果一個視圖使用了某種布局方式豺憔,那么盡量保持統(tǒng)一中方式改變布局额获。
3 不同布局方式混用
不是說可以混用嗎,怎么又突然說對同一個視圖使用一種方式恭应?那我們來定義一下混用方式: 父視圖A包含子視圖B抄邀、C,子視圖B包含子視圖B1昼榛、B2境肾,子視圖C包含子視圖C1和C2。如圖:
對于B和C我們可以使用Frame的方式布局其在父視圖A中的位置和大小胆屿,但是對于B1和B2奥喻,我們可以使用Auto Layout的方式來布局其在父視圖B中的尺寸和位置關系。這就是我理解的混用非迹,對于同一層級的視圖使用相同的布局方式环鲤,對于不同層級的使用可以使用其他布局方式。
那么它的意義是什么呢憎兽?不同view的復雜度決定了我們采取哪種方式來對其布局冷离,我們在開發(fā)中有可能會遇到某些流式的布局吵冒,我們用Auto Layout可以行云流水,但是對于某個視圖當中極其復雜的小控件的布局酒朵,Auto Layout顯然要寫不少的一大段桦锄,當兩者組合時也就是混用的意義所在。
4 使用過程中的坑
(1) 約束變化后視圖更新
有的時候我們更改約束后發(fā)現(xiàn)視圖并沒有變化蔫耽,這是因為約束改變后并沒有觸發(fā)視圖重新布局结耀。如果要重新布局,可以使用layoutIfNeeded()立即更新視圖布局匙铡,使用setNeedsLayout()在下個繪圖周期中觸發(fā)布局更新图甜。這兩個方法都會觸發(fā)layoutSubviews()方法。
(2)不要啟用NSAutoresizingMaskLayoutConstraint
蘋果iOS6之后推出了Auto Layout技術鳖眼,但是還想兼容原來的autoresizing黑毅,所以它將autoresizing轉化為了約束,轉化后的約束類型就是NSAutoresizingMaskLayoutConstraint钦讳,這就有可能導致系統(tǒng)為我們添加了許多我們無法預料的約束矿瘦。當我們添加的約束不,但是視圖顯示在界面上沒有任何問題愿卒,這是NSAutoresizingMaskLayoutConstraint幫我們自動補齊了約束缚去。在此情況下有可能出現(xiàn)frame不正確。所以我們在混用時不要開啟此屬性琼开,即設置其為NO易结。在storyboard上NSAutoresizingMaskLayoutConstraint默認為開啟,純代碼默認為關閉柜候。
總結:
(1)使用Auto Layout時NSAutoresizingMaskLayoutConstraint設置為NO搞动,設置完整清晰的約束。
(2)對同一層級視圖使用相同布局方式渣刷,對非同一層級視圖可以使用不同布局方式鹦肿。對同一視圖在同一布局時間段內不要同時使用不同的布局方式。
(3)搞清視圖布局飞主、繪制步驟狮惜,區(qū)分清楚Frame和Auto Layout的有效范圍,在合適的時機進行視圖布局變化碌识、更新和frame獲取碾篡。