# ios AutoLayout 技術實踐
前言
很久沒用autolayout,一直用的masonry,再用autolayout,很生疏,寫一篇文章,作為手記。
碼字較多,確實令人不想看,希望看到的小夥伴認真讀一下.
ofollow,noindex">Demo
masonry
個人比較喜歡標哥關於masonry的見解: 部落格
sb,xib
具體sb,xib的細節這裡就不多瞭解了。下面推薦一篇文章,看下就行了
iOS進階—SB和XIB的前世今生
xib 使用
xib 原理
這篇文章不錯,至少之前沒有了解到這一點
xib 轉程式碼
這裡xib轉純程式碼,覺得沒多大意義,複雜頁面轉後,會想吐的。不過了解一下也好,初學者,可以將ib檢視轉為程式碼,學習控制元件如何使用的。
xib 約束
檢視約束,不管masonry還是AutoLayout。深入理解約束一詞的意思,萬變不離其宗。即把一個檢視束縛在一個地方,當頁面變化時候,檢視不會亂跑。檢視變化只有“上下左右”四個維度,仔細理解寬高的變化其實也是上下左右中的變化,所以,怎樣束縛一個檢視?
1:確定origin:{x, y}
2:確定frame:{width, height},或確定右邊下邊,也相當於確定了寬高。
所有就會有各種組合:
上左下右
上左寬高
左寬centerY高
centerXY寬高
。。。
autolayout
關於autolayout的基本瞭解,這裡不多說, 推薦簡書-一天一點xib系列
控制元件集

viewarea.png
看這一大片,實際專案中,能用40%就不錯了,這篇文章就揀一些常用的瞭解
顯示區

displayarea.png
這裡能看到左邊是檢視層次結構和約束,中間是視覺化頁面,右邊是佈局操作區。點點看,沒多少東西
約束操作區

handlearea.gif
開發中,常用的也就右下角那幾組約束功能。一直用的都是他們
第一個標籤
Update Frames.單一功能,就是恢復檢視原佈局的。跟git上的reset、svn的revert差不多,理解成返回就行了
第二個標籤
Embed In Stack.這是Xcode7在iOS9引入的新功能,它用來統一管理它所有的subView(子檢視)上的約束.相當於一個容器view用來統一管理他所有subView的約束,其實普通的UIView也可以作為容器view來管理其subView的約束,我們之前做複雜UI顯示邏輯的時候往往也會放一個背景的容器view,stack view就是起到這個作用,意義不是很大,它做的事情UIView也可以做,但是他的優勢在於:可以通過設定屬性的方式讓系統自動新增對其subView的約束,而且該view是不渲染在頁面上的,對它設定背景色等屬性是無效的...(對android有了解的,這個跟merge差不多)
第三個標籤
Align.用於新增多個控制元件間對齊關係,從上到下依次是左對齊、右對齊、上對齊、下對齊、水平對齊、豎直對齊,這些現在都是灰色的不能選擇,只有同時選中多個控制元件,他們才是可用的,或者先選擇一個控制元件,然後按住control拖動到另一個控制元件上,就會彈出一個控制元件對齊的視窗,可以在裡面設定兩個控制元件的對齊關係。下面兩個是相對於superView設定水平、豎直居中,選中單個控制元件就可以設定
第四個標籤
Add New Constraints.用於對單個控制元件設定約束,上面的四個框分別填寫上下左右的約束,注意,每個框右側的三角是可以點擊出選單進行選擇的,比如有A、B、C三個控制元件,A、B同在C的左側,對C設定左側約束的時是可以選擇針對A還是B進行計算的,如果通過auto layout設定的約束與顯示的結果不符的時候,可以點選三角檢查是不是設定約束的參照物件選錯了。
Constrain to margins選項的解釋:
當拖動一個控制元件到另一個控制元件裡時,作為super的控制元件會有幾條參考線(藍色虛線,如果你使用的硬體是帶有Force Touch觸控板,且使用的xcode7的時候,拖動到參考線處的時候觸控板會輕微震動,發出機械上的一個聲音以給你反饋),上下左右四個方向的邊緣會有,水平、豎直的中心處會有。
若勾選Constrain to margins實際super與sub之間的參考邊緣就是這些參考線,而不是實際的super的frame的邊緣,如果我們不勾選的話就是以frame的邊緣為參考。
下面的Width、Height是限制自身寬高的選項。
Equal Widths、Equal Heights是與其他控制元件保持相同的寬高,預設是灰色不可用狀態,只有選擇兩個以上的控制元件,才可使設定,同樣也可以先選中一個控制元件,按住control拖動,彈出的視窗中也有該選項。Aspect Ratio 是設定自身的寬高比的。
Align選項同樣是設定兩個以上控制元件的對齊關係的。
Update Frames一般是用來更新frame的。我們設定的約束如果與當前控制元件的frame產生衝突的時候就要解決衝突,要麼修改約束,要麼修改frame,最後使系統可以沒有歧義的確定UI佈局,有衝突的時候會在xib左邊欄的右上角顯示警告或錯誤的標識,我們點選標識,按照系統提供的衝突解決方法就可以解決衝突。
第五個標籤
Resolve autoLayout issue.這個標籤主要用於重新設定autolayout.這個標籤可以作用在選中的view或者是以這個view為父檢視的所有view
比如:Update Frame,用於更新UI,比如我們設定了自動適應佈局,可以用這個選項來更新它的位置.
Clear Constraint,用於清空所有的約束.
注意
如果我們使用了autoLayout自動佈局,那麼我們在ViewDidLoad和iOS5之後新加入的ViewWillLayoutSubviews中修改Frame均不能生效.這是因為,ViewWillLayoutSubviews這個方法在ViewDidLoad之後呼叫,也就是說frame生效之後接著就被autoLayout給重新佈局了.
既然這樣那麼,我們要更改frame就要在ViewDidLayoutSubviews中更改,或者將自動佈局拖成屬性,在程式碼中更改.
屬性設定區

propertysettingarea.gif
這裡面絕大部分設定,用純程式碼都能編寫。所以,這裡設定,轉為純程式碼,去了解學習控制元件屬性知識也是不錯的辦法
第一個標籤
show the file inspector.這個標籤主要介紹xib檔案的基本資訊,一般是不會用到的,所以也不用修改.
第二個標籤
show quick help inspector.這個標籤就是一些快捷幫助資訊,基本上就是蘋果API中對某個控制元件的介紹.
第三個標籤
show the identity inspector.在這個標籤下主要做一些標識.我們最常用的就是其中的Custom Class,用這個標籤來關聯xib檔案與我們自己建立的類檔案
第四個標籤
show the attributes inspector.在這個標籤使我們最常用的一個標籤,我們通常會使用它進行控制元件的屬性設定.比如設定模擬器的一些尺度,顏色等相關的.這個標籤的內容(即可設定的屬性)會因控制元件的不同而變化的.
第五個標籤
show the size inspector.這個標籤是設定frame的相關,主要與尺寸相關.
第六個標籤
show the connections inspector.這個標籤主要負責xib檔案與類的原始檔互動,通俗的將就是"連線",在xib中控制元件的屬性與觸發的動作,都是可以拖一條線到類的原始檔中,用程式碼進行下步操作的.這會在接下來進行介紹.
實踐出真知
下面通過專案中常用的控制元件約束逐一講解xib中autolayout的使用
label
一般約束
第一次使用autolayout,先拖一個label試試
這是xib中autolayout佈局的一個label,整個操作如圖。

labeltry.gif
這是用三方約束庫masonry進行約束的,看程式碼,剛設定的background和text用純程式碼寫都是一一對應的,所以,這裡告訴你,xcode的視覺化ui佈局做的很棒,開發中一般用到的檢視屬性設定,這裡都能找到設定的地方

labelmasonry.png
這是用最古老的純程式碼編寫ui佈局。看下程式碼量,如果寫個稍微複雜點的頁面,是不是會覺得不爽。

labelframe.png
從上面可以看出,純程式碼開發的,就不過多討論了,比較masonry和autolayout佈局。用哪個呢?autolayout能快masonry幾條街吧。
最後,針對剛入門的ios開發來說,建議先masonry,再autolayout。因為autolayout的實現,你並不知道oc程式碼的具體實現,不利於修煉內功。(當然,外包的話,你是沒有那個條件的,只能用autolayout,只為快,只是重複)
內邊距需求
專案中label一般clear背景展示內容,很少有內景色的,但是如果美工設計需要有內邊距的。但是autolayout中並沒有內邊距設定呀?

labelattributed.png
看到label的attributed型別是不是有點聯想,我們程式碼實現label的行間距,字型顏色等富文字設定的時候,用的就是這裡的一些東西。但是沒有找到能設定內邊距的。這種需求可以通過自定義label實現,如下:
實踐後發現,這種方式內容是顯示不全的,這裡沒有深究,有搞過的朋友,可以留言交流。。。☺

labeledge.png
總結:
uilable 自適應高度,是不帶內邊距的。如果非要實現上面的方案也有,只是內容顯示不全。使用TextView是完美的替代方案。下面會講textview的用法
補充
如果 nib 或 storyboard 裡用了 autoLayout,實際執行順序是先執行viewDidLoad再執行 autoLayout
定製邊框需求
檢視的邊框需求,在專案中大多會用到,我們一般處理方式為view.layer的操作。但是autolayout如何實現呢?
User Defined Runtime Attritubes:使用者定義執行時屬性

bordertool.png
key path。有些感觸吧,oc的kvc編碼,所以,物件的屬性都可以在這裡試試看。只是有些特例無法直接實現,如boder.color。。
注意:runtime,看到了吧,所以,xib中是無法視覺化的。執行起來才能看到效果。
特例:設定邊框顏色
layer.borderColor type裡只有Color 沒有CGColor。
解決:CALayer分類 提供方法:transeColor2CGColor:(UIColor *)color。方法寫不寫在.h裡面都無所謂
使用:layer.transeColor2CGColor
注意:正常來講,分類裡是沒有屬性的,但是在使用時候,就當有屬性使用,所以,方法名一般為setAbc。然後xib中設定時候使用是abc,如下:

CALayer+Color.png

layerbordercolor.png
實際操作時候,儘量還是用copy,手敲的話,很容易手誤,又很難發現。如果設定錯誤,系統預設為黑色。
textview
高度自適應需求
針對label,textview等需要根據文字內容,高度自適應,可以使用純程式碼計算內容高度,但是大家應該也比較詬病這種方法,諸如開發麻煩,計算不準等。使用autolayout相對來說有種方案就簡單多了,如下:

textviewadjustheight.gif
看效果:

label2textview.png
這裡有兩點注意:
1:設定過後,看上圖,高度約束變虛線了。
2:用過masonry都知道,每條約束都有優先順序,因為masonry也是封裝的NALyoutConstraint嘛,看下圖,不解釋。

constraintpriority.png

masonrypriority.png
總結:
在內容自適應高度的佈局需求中,這種佈局方式也是一大利器。請善用
1:有內邊距
2:內容預設吸頂
3:如果需要自適應高度,調整高度自適應,不讓滾動即可實現
需求拓展
在實際開發中,textview不能一直隨著內容增高,會有一個最大高度。實際開發中,我們一般使用封裝好的自定義textview。如下,這裡講autolayout,不過多的瞭解如何自定義
button
通過上面label的練習,大部分單個檢視的約束操作都是能應對的。下面以button為例項,瞭解一下不同狀態的練習。
button 基本設定:背景色、背景圖片、圖片、標題、圓角、狀態:正常,高亮,選中,等
多操作,熟能生巧,xib中ui佈局都是操作性的
imageview
imageview基本操作這裡就略了,跟上面一樣,下面說一些進階的:
imageview新增子檢視
沒有實際操作過的,並不覺得這裡有什麼,坑點就是imageview上沒法新增子檢視。純程式碼你也可以實踐試試。下面說下xib如何實現:

superimageview.png
imageview 新增子檢視:拖拽uiview檢視,class 改為imageview,由於是UIView,要在.m中設定圖片
總結:
碰到檢視巢狀的需求,如果父檢視不支援巢狀,可以試試這麼幹。把UIView轉為對應子型別
tableview
終於說到tableview,初入門ios的,感覺tableview有些懵逼吧,什麼協議,代理,資料來源等一些新名詞,整的費解。不過,這裡說的是佈局,嘿嘿
展示全部內容
tableview本身是可以滾動的,如果cell不多,想展示全部內容共,讓tableview根據cell自適應高度。參考下面collectionview部分。demo中有實現。
cell自適應高度
我們實際開發中共,cell自適應高度一般使用的都是三方庫:
xib佈局對應: UITableView-FDTemplateLayoutCell" target="_blank" rel="nofollow,noindex">UITableView-FDTemplateLayoutCell
masonry佈局對應: MasonryAutoCellHeight" target="_blank" rel="nofollow,noindex">HYBMasonryAutoCellHeight
這裡瞭解一下,不用三方庫,實現cell自適應高度的操作:self-sizing cell,實在不想碼字了,看demo實踐
collectionview
如何顯示collectionview全部內容 這裡有個論題,可以看看。實踐發現,方案如下:
方案:
collectionView的contentSize.height賦給collectionView的高度約束。
總結:
collectionview作為複雜頁面的子檢視,如果需要展示全部cell,這種方案是比較好的。tableview也同樣道理。如果有需求,就可以這麼幹。。
1:設定collectionview不可滾動
2:設定collectionview高度約束
注意:獲取contentSize一定要在cell載入完成後,不然獲取到為0。collectionview是不會顯示的,cell也不會載入。
scrollview
開發中,有些頁面,無規律沒法用tableview,但是有很長,超過了螢幕,我們首選scrollview。說到scrollview,感覺是比較難用的,特別是masonry佈局時候,有些麻煩。這裡瞭解一下,提供一個不錯的方案。
scrollview約束
因為scrollview是可以滾動的,所以有個自身的frame,還有個並不存在的contentview,就是scrollview裡的子檢視們。想象一下放映機,放映口和膠片。放映口相當於scrollview的frame,膠片相當於scrollview內的內容。一旦膠片長度超過放映口。是不是就有滑動的觀感了。tableview和collectionview和textview都是繼承自scrollview,所以,都是同樣的道理。如何實現?原理就是scrollview新增containerview。子檢視們撐開containerview,將constainerview的size作為scrollview的contentsize。
目的:確定 scrollview 的 contentSize
三步:
1:新增scrollview,並約束
2:新增containerview,並約束

scrollviewconstraint.gif
理想情況這裡應該正常的啊,然而,看一下約束錯誤。表示scrollview沒法確定x方向的position或width,和y方向的position或height。
3:約束解決
我們目的是確定scrollview的contentsize。那麼即是確定containerview的size{width,height}和origin{x,y}。如下:
1):width。同scrollview的frame的width即可。已經約束過
2):xposition/width。假設需求是豎直滾動。那麼設定水平居中是最合理的。因為scrollview中content的左右相對約束點你都不知道。containerview的左右相對scrollview已經約束過,怎樣組合,水平方向才能束縛住containerview不亂跑呢?一是設定containerview的width,二是水平居中,試試看:

scrollviewconstraintsolvehor.gif
3):yposition/height。由於需求是豎直滾動,所以,不能豎直居中,只能約束containerview的height。

scrollviewconstraintsolvevertical.gif
scrollview滑動
以上scrollview的約束做過了。知道原理,scrollview以什麼樣的frame存在或巢狀都是可以應對的。西面瞭解下開發中常見的場景:
subviews超過一屏,使用scrollview實現滾動:
方案:手動計算更新containerview的height約束。
這種虛擬器視窗不能變動

simulatedmetricsinferred.png
調整metrix

simulatedmetricsfreeform.png
調整虛擬視窗,拉大高度

simulatedmetricsscroll.png
手動計算-動態設定containerview的高度

handlecontainerviewheight.png
控制元件平分
上面瞭解了單個檢視的佈局。下面瞭解一下多檢視組合佈局的技能。
需求:檢視等寬等間距平分父檢視

viewdivide.gif
比例佈局
實際開發中,比例佈局用的感覺不是很多。這裡也瞭解一下:

multiplier.gif

aspectratio.gif
這個第二類約束,解釋一下:
約束過紅view的寬高ratio後,約束報錯,是因為已經約束了寬高,現在又約束寬高比1:2,跟原寬高比不一樣,所以,刪除寬高的任一約束即可。
組合批量處理
先看個錯誤的栗子。

grouphandleerror.gif
green的view為啥跑到前面去了呢?而不是4個view左右平分。看下green檢視的相對約束,發現是相對於safe area。為啥呢 ?因為幹剛開始放的時候,green檢視就非常靠上,該檢視左右能看到的第一個檢視就是safe area。所以,就這樣了。這也是autolayout方便的一點,預設檢視依賴,下面看正確的處理。。

grouphandle.gif
解釋一下:組合批量處理分3個步驟:
1:預先處理
上面錯誤例子演示了,首先要把幾個檢視放到差不多的位置,然後再進行整體處理
2:劃整體處理
將檢視組處理為一個整體:
1:水平對齊
2:左右平分,等寬等高
3:整體約束
上面將檢視化為一個整體了,剩下的,就單選任一個檢視,確定yposition/height即可。這裡操作的是blue檢視,topSpace和height。這樣就確定了每個檢視的上下左右或寬高四個約束了
組合自適應佈局
這種佈局的場景一時沒想起來,沒遇到過。這裡就不多瞭解了。推薦一篇好文:
子檢視撐開父檢視
這類佈局需求太常見了,總想有騷操作來實現,實際開發中總是用笨方法,計運算元檢視高,更新父檢視高。這一點都不智慧啊:用autolayout下面有一種方法,一起了解一下:

groupadjust.gif
解釋:
高度自適應
設定約束,記住規則:先父後子,父無高需自適,子一一約束四方,最後父依賴最底檢視的底部。
圖中的子檢視,由於自適應內容,所以特殊設定了高,priority。上面label,textview有了解過。
priority設定多大?
系統提供的有Required:1000,High:750,Low:250。這裡設定時候取值範圍:0 < x < 750 & x != 250。
看效果:

groupadjustresult.gif
基於Object封裝
推薦文章。。這裡有瞭解object的使用。我也是照著實踐的,所以,就不搬磚了
基於UIView封裝
基於UIView封裝xib: xib 還沒那麼強大:
1:如果想實現vc.xib自動初始化customview。那麼,vc和view就沒法共用view中的互動
封裝自定義view步驟 1):customview.xib不設定同名class 2):customview.xib設定file's owner為同名cutomview類 3):在vc.xib直接使用view檢視,設定class為customview即可
2:如果想實現vc和customview共用customview中的互動。那麼,vc中需要手動載入customview.xib。並約束。
封裝自定義view步驟 1):customview.xib設定同名class 2):customview.xib設定file's owner為所在vc類 3):在vc中載入customview.xib。按需約束。
1):約束不能和vc.xib統一約束,可以masonry和autolayout共同約束,所以,一般可用在單一約束上,沒有其他customview的相對約束,比如:tableview的headerview。
2):customview.xib不能放到customview中initwithcoder進行初始化載入。只能使用的地方手動載入。因為:customview.xib的file's owner是vc
沒有圖示,是不是看的雲裡霧裡的。真正實踐過的,應該知道我在說什麼。看到這裡的有興趣的朋友,一起交流。
xib 高深用法
這裡詳細講解了xib一些高階用法,個人瞭解較少,就不亂說了
總結
一起了解了autolayout的一些基本和進階用法,autolayout操作都是技能性的,多練習就ok了。這裡不能窮舉所有的用法,學會自己摸索,根據約束錯誤提示,練習自己的約束方案和習慣