隨着iPhone6/6+設備的上市,如何讓手頭上的APP適配多種機型多種屏幕尺寸變得尤其迫切和必要。(包括:iPhone4/4s,iPhone5/5s,iPhone6/6s,iPhone 6p/6ps)。html
在 iPhone6出現之前,咱們接觸的iPhone屏幕只有兩種尺寸:320 x 480和320 x 568。因此在那個時候使用傳統的絕對定位(Frame)方式進行界面控件的佈局仍是比較輕鬆的,由於咱們只須要稍微調整一下Frame就能夠適配這兩種 大小的屏幕了。也許這也是爲何雖然AutoLayout從IOS6就已經出現了,可是對於AutoLayout的使用和普及好像都不怎麼火熱。不過直到 最近隨着iPhone6/6+設備的出現,AutoLayout又被衆多開發者從新審視和重視了。畢竟APPLE推出AutoLayout就是爲了幫助開 發者的APP更方便簡單的適配未來不一樣蘋果設備的不一樣大小屏幕。ios
首先咱們來看一下APPLE官方是如何描述Auto Layout的:Auto Layout 是一個系統,可讓你經過建立元素之間關係的數學描述來佈局應用程序的用戶界面,是一種基於約束的,描述性的佈局系統。因此咱們如今要開始摒棄使用傳統的 設置 frame 的佈局方式的思惟來開發視圖界面了。由於在 Auto Layout 中,當你描述完視圖對象之間的約束以後, Auto Layout 會自動幫你計算出視圖對象的位置和大小,也就間接的設定了視圖的Frame。反過來,若是咱們還使用傳統的絕對定位的方式,經過設定視圖的Frame來布 局的話,那麼隨着蘋果設備屏幕尺寸的碎片化,那麼每一種屏幕尺寸都要給界面控件設定一套合適該尺寸的Frame,這種方式想一想就夠嚇人的!另外還須要說明 的是,現在確實還有很多人仍然使用設定Frame的方式進行佈局,而且經過取設備屏幕的寬高進行必定比例的換算確實能夠達到正確的定位佈局,可是在大多數 狀況下,若是頁面支持屏幕旋轉的話,這種設定Frame的方式就徹底失效了,旋轉屏幕須要作大量的額外處理。還有一點是咱們必須注意的,不少人習慣上是在 viewdidload方法中初始化控件(包括init 和設置frame),可是viewController要在viewWillLayoutSubviews的時候才能真正肯定view和子view的 frame。因此在沒有肯定view的frame的時候就去操做修改view的frame是不友好的,設置frame的效果也是沒法預料的。因此按照這個 特性,若是咱們用設置Frame的方式佈局的話就必須在viewWillLayoutSubviews中從新設定view的座標大小,佈局邏輯會變得更不 清晰。而使用AutoLayout則不會有這些問題。git
那麼接下來咱們來說一下如何使用AutoLayout。github
你們都應該清 楚,咱們能夠在XIB、StoryBoard中經過拉線的形式給控件視圖添加布局約束,經過蘋果強大的可視化界(Interface Builder)咱們可以輕鬆的使用AutoLayout完成界面視圖的佈局。另一種方式就是經過純代碼的形式使用AutoLayout,即 NSLayoutConstraint。本人是個代碼控,我的比較傾向於代碼寫界面,因此本文主要講一下最近本人經過純代碼的方式使用 AutoLayout和使用第三方界面佈局庫Masonry進行代碼佈局的總結和分享。數組
首先談一下在現在AutoLayout的時代,是使用XIB、StoryBoard好些仍是使用純代碼佈局好!?本人根據本身的經驗以爲,這個沒有一個絕對的界限或者什麼一刀切。可是在權衡這個問題的時候,我我的以爲有幾個原則應該要去遵照的:框架
一、在一些比較簡單、固定的界面。好比登陸、註冊或者其餘只是進行內容展現的界面使用XIB、StoryBoard開發起來會更簡單快一些,這個時候咱們也應該使用XIB、StoryBoard開發。函數
二、 在一些複雜、控件較多和功能多的界面儘可能使用代碼進行佈局開發。由於控件多功能複雜的界面若是使用XIB、StoryBoard。那麼經過拉線的形式添加 約束佈局,你們應該都有經歷過,一個XIB里拉滿了密密麻麻的約束線,能夠確定的是過不了多久連本身都看暈了。若是這個模塊要交給第二我的維護,那麼這些 密密麻麻的約束線確定是一個讓人頭疼的問題。由於XIB中約束過多的話,首先可讀性是很是差的,帶來的後續問題是開發思路不清晰、維護難。佈局
三、須要複用的模塊儘可能使用代碼佈局。若是使用XIB、StoryBoard則沒法很好的對代碼模塊進行復用。動畫
NSLayoutConstraint 篇ui
進入正題,咱們首先來談一下如何使用官方提供的API(NSLayoutConstraint)進行代碼佈局。
談 到NSLayoutConstraint,你們都有一個不怎麼好的感受。哎,能夠確定的是APPLE一直在推AutoLayout。只是貌似在可視化的布 局設計(XIB、StoryBoard)下的力度和功夫遠比代碼佈局要大。由於經過APPLE提供的API進行代碼佈局確實不怎麼好用,可是仍是在能夠接 受的範圍,呵呵!
1、Autoresizing Mask
在使用AutoLayout以前咱們先介紹Autoresizing Mask。
必需要注意的是在使用 Auto Layout 時,首先須要將視圖的 setTranslatesAutoresizingMaskIntoConstraints 屬性設置爲 NO。這個屬性默認爲 YES。當它爲 YES 時,運行時系統會自動將 Autoresizing Mask 轉換爲 Auto Layout 的約束,這些約束頗有可能會和咱們本身添加的產生衝突。 咱們經常會忘了作這一步,而後引發的約束報錯就是這樣的:
因此若是你是使用 Xib/StoryBoard 的話,系統會自動幫你把這個屬性設置爲 NO。經過Interface Builder,打開某個Xib或者StoryBoard,在右側Show in file inspector裏面就能看到Ues Autolayout選項,系統默認將其勾選。以下圖:
即在XIB上開啓Autolayout後,autoresizingMask就被廢棄了。避免了約束衝突的狀況。
若是你是經過代碼佈局的話,在給view添加約束以前,只須要經過代碼把view的 setTranslatesAutoresizingMaskIntoConstraints 屬性設置爲 NO。
1 2 |
|
2、NSLayoutConstraint
Auto Layout 中約束對應的類爲 NSLayoutConstraint,一個 NSLayoutConstraint 實例表明一條約束。
NSLayoutConstraint有兩個方法,咱們主要介紹 constraintWithItem:也是最經常使用的:
1 2 |
|
這 個API給咱們的第一印象就是參數有點多。其實仔細一看錶達的意思無非就是:view1的某個屬性(attr1)等於view2的某個屬性(attr2) 的值的多少倍(multiplier)加上某個常量(constant)。描述的是一個view與另一個view的位置和大小約束關係。其中屬性 attribute有上、下、左、右、寬、高等,關係relation有小於等於、等於、大於等於。須要注意的是,小於等於 或 大於等於 優先會使用 等於 關係,若是 等於 不能知足,纔會使用 小於 或 大於。例如設置一個 大於等於100 的關係,默認會是 100,當視圖被拉伸時,100 沒法被知足,尺寸纔會變得更大。
那麼下面咱們來看一下,如何運用NSLayoutConstraint進行代碼佈局。
場景一:
假如咱們設計一個簡單的頁面。一個子view在父view中,其中子view的上下左右邊緣都離父view的邊緣40個像素。這個咱們該如何寫呢?以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
效果如圖:
這裏講一下比較容易犯錯的地方:
一、添加約束前肯定已經把須要佈局的子view添加到父view上了
二、必定要禁止將Autoresizing Mask轉換爲約束
三、要把子view的約束加在父view上
四、由於iOS中原點在左上角因此使用offset時注意right和bottom用負數
場景二:
子view在父view的中間,且子view長300,高200。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
效果如圖:
這裏須要注意的是:
一、若是是設置view自身的屬性,不涉及到與其餘view的位置約束關係。好比view自身的寬、高等約束時,方法constraintWithItem:的第四個參數view2(secondItem)應設爲
nil;且第五個參數attr2(secondAttribute)應設爲 NSLayoutAttributeNotAnAttribute 。
二、在設置寬和高這兩個約束時,relatedBy參數使用的是 NSLayoutRelationGreaterThanOrEqual,而不是 NSLayoutRelationEqual。由於 Auto Layout 是相對佈局,因此一般你不該該直接設置寬度和高度這種固定不變的值,除非你很肯定視圖的寬度或高度須要保持不變。
3、更新/修改約束
Auto Layout 的更新、修改約束操做,也不怎麼友好和方便。
先來看一下 NSLayoutConstraint 的API,貌似只有constant屬性能夠修改,其它都是隻讀的。因此對於現有約束的修改和更新都是圍繞NSLayoutConstraint實例的constant屬性展開的。
一、若是你是使用 Xib/StoryBoard 的話,在程序運行時想動態的改變視圖的約束,你能夠這樣作:
約束Constraint也能夠像控件同樣作IBOutlet連接,經過拖線關聯到文件。(事例這裏是改變子view相對於父view的top約束)
首先在你的ViewController的頭文件裏定義一個約束屬性:
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *topConstraint ;
1 2 3 4 |
|
而後在XIB裏選中 File's Owner,選中Outlet欄目。將對應的Outlet拖動到View對應Constraint鏈接起來就能夠了。跟button/label作連接一摸同樣的。
這樣咱們就能夠得到view上面的某個具體約束了,而後就能夠在文件中對這個約束進行咱們想要的修改。
1 2 3 |
|
總結來講就是:拖線關聯到文件得到約束,修改約束的constant屬性。
二、若是你是使用 Xib/StoryBoard ,可是不想經過上述拉線的方式得到約束而後再去更新它,你還能夠採用代碼的方式修改約束:
可是不管採用哪一種方式,咱們都要遵循Auto Layout 的約束更新機制:想要更新視圖上面的約束,就要先找到對應的約束再去更新它。遍歷view上面的全部約束,查找到要更新的約束再進行更新。
因此咱們要像上面同樣要先得到top約束。在代碼中的體現就是經過約束的標識字段,在其父view的constraints數組中遍歷查找。這是由於每一個view的constraints數組中保存的其實是 layout 子view所需的約束的集合。
咱們能夠經過下面的輔助函數實現:
1 2 3 4 5 6 7 |
|
這裏只判斷了一個標識字段(NSLayoutAttributeTop),可是大多數狀況下view上面的約束依賴不會那麼簡單,因此須要查找判斷多個標識字段,才能找到。
三、若是你是使用代碼佈局,那就用上面2介紹的方法更新約束,或者在添加約束以前先將該約束記錄下來,方便後面的更新修改。
4、Auto Layout 關於更新約束的幾個方法
setNeedsLayout:告知頁面須要更新,可是不會馬上開始更新。執行後會馬上調用layoutSubviews。
layoutIfNeeded:告知頁面佈局馬上更新。因此通常都會和setNeedsLayout一塊兒使用。若是但願馬上生成新的frame須要調用此方法,利用這點通常佈局動畫能夠在更新佈局後直接使用這個方法讓動畫生效。
layoutSubviews:系統重寫佈局。
setNeedsUpdateConstraints:告知須要更新約束,可是不會馬上開始。
updateConstraintsIfNeeded:告知馬上更新約束。
updateConstraints:系統更新約束。
這麼多方法中,目前我使用比較多的是 layoutIfNeeded 。由於在Auto Layout 實現動畫的時候,layoutIfNeeded 方法能夠馬上生成新的frame特性是一大利器。
5、使用 Auto Layout (NSLayoutConstraint)實現動畫
目前貌似還有不少人對於 Auto Layout 的動畫實現還不是很瞭解。畢竟之前咱們處理動畫之類的交互大都是和view的frame屬性打交道,即便用傳統的 animation方法修改view的frame。大體相似於
1 2 3 4 5 6 7 |
|
那 麼在Auto Layout下咱們又該如何處理相關的動畫呢?根據前面說到的Auto Layout佈局約束的原理,在某個時刻約束也是會被還原成frame使視圖顯示,這個時刻能夠經過layoutIfNeeded這個方法來進行控制,可 以馬上生成新的frame並展現出來,從而實現動畫效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
這樣咱們就能夠經過 AutoLayout 實現傳統的animation方法。須要注意的是在調用animation方法以前必定要先調用layoutIfNeeded,這是爲了讓view先生成最初的frame並顯示,不然動畫效果會失效。
Masonry 篇
Masonry 是一個輕量級的界面佈局框架,擁有本身的描述語法,採用更優雅的鏈式語法封裝自動佈局,簡潔明瞭並具備高可讀性,並且同時支持 iOS 和 Max OS X。Masonry是一個用代碼寫iOS或OS界面的庫,用官方的說明就是Masonry完成能夠代替Auto layout。Masonry的github地址:https://github.com/SnapKit/Masonry
Masonry使用起來很方便和流暢,本人最近開始在新項目中使用框架進行界面佈局。親身的實踐感受Masonry確實比APPLE的官方的API(NSLayoutConstraint)好用不少。先來看一下Masonry官方的提供的sample
code:
1 2 3 4 |
|
這也是最經常使用的用法,爲view設置約束。 看到上面的代碼風格,典型的鏈式語法,流暢易懂。
咱們先來看一下Masonry支持的約束屬性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
這 裏須要注意的是:NSLayoutAttributeLeft/NSLayoutAttributeRight 和 NSLayoutAttributeLeading/NSLayoutAttributeTrailing的區別是left/right永遠是指左右,而 leading/trailing在某些從右至左習慣的地區會變成,leading是右邊,trailing是左邊。因此若是涉及到國際化方面,建議仍是 使用 NSLayoutAttributeLeading/NSLayoutAttributeTrailing。
在Masonry中可以添加、修改 Auto layout 約束有三個函數:
[objc] view plaincopy
1 2 3 |
|
其中:
mas_makeConstraints 只負責新增約束,Autolayout不能同時存在兩條針對於同一對象的約束,不然會報錯。(這個方法最經常使用)
mas_updateConstraints 針對上面的狀況會更新在block中出現的約束,不會致使出現兩個相同約束的狀況。
mas_remakeConstraints 則會清除以前的全部約束 僅保留最新的約束。
若是咱們靈活的運用這三個方法,基本就能夠應付各類各樣的約束佈局狀況了。
1、添加約束(mas_makeConstraints)
先來看一下Masonry如何實現一個view的簡單佈局。
場景一:
仍是和上面的例子同樣:一個子view在父view中,其中子view的上下左右邊緣都離父view的邊緣40個像素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
針對上面的佈局約束寫法,還有更爲簡潔的寫法:
1 2 3 4 5 |
|
效果以下:
能夠很明顯的看出,使用Masonry佈局不管是代碼量仍是語法描述都很簡潔易懂。比起前面使用 NSLayoutConstraint 不是好一點半點。
場景二:
子view在父view的中間,且子view長300,高200。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
效果以下:
2、更新、修改約束(mas_updateConstraints)
使用Masonry更新約束很是方便簡單。
好比須要將上面例子的view的寬度和高修都改成100:
1 2 3 4 5 6 |
|
3、在使用Masonry中,咱們須要注意幾個問題
一、在使用 mas_makeConstraint 方法給view添加約束的時候,必需要肯定該view已經添加到父view上了,即[self.view addSubview:view];不然將會約束報錯。這個和使用NSLayoutConstraint同樣。
二、Autolayout不能同時存在兩條針對於同一對象的約束,不然會報錯。只能進行更新修改。
三、 其次對於 equalTo 和 mas_equalTo的區別:mas_equalTo只是對其參數進行了一個BOX操做(裝箱) ,所支持的類型除了NSNumber支持的那些數值類型以外就只支持CGPoint、CGSize、UIEdgeInsets,例 如:make.size.mas_equalTo(CGSizeMake(300,400));
對於對象或是多個屬性的處理,就使用equalTo,例如:make.size.equalTo(weakSelf.view); make.width.equalTo(weakSelf.view); make.height.equalTo(@30);
四、方法with和and,這連個方法其實沒有作任何操做,方法只是返回對象自己,這這個方法的左右徹底是爲了方法寫的時候的可讀性 。
五、由於iOS中原點在左上角因此使用offset時注意right和bottom用負數。
六、Masonry約束是沒法更新 NSLayoutConstraint 約束。由於Masonry在更新約束的時候會去遍歷查找view上面的約束集,先判斷view上的約束的類是否爲 MASLayoutConstraint的 類,若是是纔會進行更新。因此,若是你是用XIB、StoryBoard拉線添加的約束或者是經過代碼方式使用NSLayoutConstraint類添 加的約束都沒法在代碼裏用Masonry的 mas_updateConstraints 方法進行約束更新。Masonry更新約束的部分源碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
4、Masonry實現動畫
Masonry的動畫實現和NSLayoutConstraint類的動畫實現基本一致,都是基於 layoutIfNeeded 方法。傳統的 animation方法經過Masonry實現以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
參考:
http://www.cocoachina.com/ios/20150702/12217.html
http://www.cocoachina.com/ios/20141219/10702.html