# ios AutoLayout 技術實踐

前言

很久沒用autolayout,一直用的masonry,再用autolayout,很生疏,寫一篇文章,作為手記。

碼字較多,確實令人不想看,希望看到的小伙伴認真讀一下.

Demo

masonry

個人比較喜歡標哥關于masonry的見解:博客

sb,xib

具體sb,xib的細節(jié)這里就不多了解了。下面推薦一篇文章,看下就行了

iOS進階—SB和XIB的前世今生

xib 使用

xib 原理

ios開發(fā)之xib的詳細加載過程

這篇文章不錯,至少之前沒有了解到這一點

xib 轉代碼

iOS進階之xib上控件自動生成純代碼

這里xib轉純代碼,覺得沒多大意義,復雜頁面轉后,會想吐的。不過了解一下也好,初學者,可以將ib視圖轉為代碼,學習控件如何使用的。

xib 約束

視圖約束,不管masonry還是AutoLayout。深入理解約束一詞的意思,萬變不離其宗。即把一個視圖束縛在一個地方,當頁面變化時候,視圖不會亂跑。視圖變化只有“上下左右”四個維度,仔細理解寬高的變化其實也是上下左右中的變化,所以,怎樣束縛一個視圖?
1:確定origin:{x, y}
2:確定frame:{width, height},或確定右邊下邊,也相當于確定了寬高。

所有就會有各種組合:
上左下右
上左寬高
左寬centerY高
centerXY寬高
。。。

autolayout

關于autolayout的基本了解,這里不多說,推薦簡書-一天一點xib系列

控件集

viewarea.png

看這一大片,實際項目中,能用40%就不錯了,這篇文章就揀一些常用的了解

顯示區(qū)

displayarea.png

這里能看到左邊是視圖層次結構和約束,中間是可視化頁面,右邊是布局操作區(qū)。點點看,沒多少東西

約束操作區(qū)

handlearea.gif

開發(fā)中,常用的也就右下角那幾組約束功能。一直用的都是他們

第一個標簽

Update Frames.單一功能,就是恢復視圖原布局的。跟git上的reset、svn的revert差不多,理解成返回就行了

第二個標簽

Embed In Stack.這是Xcode7在iOS9引入的新功能,它用來統(tǒng)一管理它所有的subView(子視圖)上的約束.相當于一個容器view用來統(tǒng)一管理他所有subView的約束,其實普通的UIView也可以作為容器view來管理其subView的約束,我們之前做復雜UI顯示邏輯的時候往往也會放一個背景的容器view,stack view就是起到這個作用,意義不是很大,它做的事情UIView也可以做,但是他的優(yōu)勢在于:可以通過設置屬性的方式讓系統(tǒng)自動添加對其subView的約束,而且該view是不渲染在頁面上的,對它設置背景色等屬性是無效的...(對android有了解的,這個跟merge差不多)

第三個標簽

Align.用于添加多個控件間對齊關系,從上到下依次是左對齊、右對齊、上對齊、下對齊、水平對齊、豎直對齊,這些現(xiàn)在都是灰色的不能選擇,只有同時選中多個控件,他們才是可用的,或者先選擇一個控件,然后按住control拖動到另一個控件上,就會彈出一個控件對齊的窗口,可以在里面設置兩個控件的對齊關系。下面兩個是相對于superView設置水平、豎直居中,選中單個控件就可以設置

第四個標簽

Add New Constraints.用于對單個控件設置約束,上面的四個框分別填寫上下左右的約束,注意,每個框右側的三角是可以點擊出菜單進行選擇的,比如有A、B、C三個控件,A、B同在C的左側,對C設置左側約束的時是可以選擇針對A還是B進行計算的,如果通過auto layout設置的約束與顯示的結果不符的時候,可以點擊三角檢查是不是設置約束的參照對象選錯了。

Constrain to margins選項的解釋:

當拖動一個控件到另一個控件里時,作為super的控件會有幾條參考線(藍色虛線,如果你使用的硬件是帶有Force Touch觸控板,且使用的xcode7的時候,拖動到參考線處的時候觸控板會輕微震動,發(fā)出機械上的一個聲音以給你反饋),上下左右四個方向的邊緣會有,水平、豎直的中心處會有。

若勾選Constrain to margins實際super與sub之間的參考邊緣就是這些參考線,而不是實際的super的frame的邊緣,如果我們不勾選的話就是以frame的邊緣為參考。

下面的Width、Height是限制自身寬高的選項。

Equal Widths、Equal Heights是與其他控件保持相同的寬高,默認是灰色不可用狀態(tài),只有選擇兩個以上的控件,才可使設置,同樣也可以先選中一個控件,按住control拖動,彈出的窗口中也有該選項。Aspect Ratio 是設置自身的寬高比的。

Align選項同樣是設置兩個以上控件的對齊關系的。

Update Frames一般是用來更新frame的。我們設置的約束如果與當前控件的frame產(chǎn)生沖突的時候就要解決沖突,要么修改約束,要么修改frame,最后使系統(tǒng)可以沒有歧義的確定UI布局,有沖突的時候會在xib左邊欄的右上角顯示警告或錯誤的標識,我們點擊標識,按照系統(tǒng)提供的沖突解決方法就可以解決沖突。

第五個標簽

Resolve autoLayout issue.這個標簽主要用于重新設置autolayout.這個標簽可以作用在選中的view或者是以這個view為父視圖的所有view
比如:Update Frame,用于更新UI,比如我們設置了自動適應布局,可以用這個選項來更新它的位置.
Clear Constraint,用于清空所有的約束.

注意

如果我們使用了autoLayout自動布局,那么我們在ViewDidLoad和iOS5之后新加入的ViewWillLayoutSubviews中修改Frame均不能生效.這是因為,ViewWillLayoutSubviews這個方法在ViewDidLoad之后調用,也就是說frame生效之后接著就被autoLayout給重新布局了.

既然這樣那么,我們要更改frame就要在ViewDidLayoutSubviews中更改,或者將自動布局拖成屬性,在代碼中更改.

屬性設置區(qū)

propertysettingarea.gif

這里面絕大部分設置,用純代碼都能編寫。所以,這里設置,轉為純代碼,去了解學習控件屬性知識也是不錯的辦法

第一個標簽

show the file inspector.這個標簽主要介紹xib文件的基本信息,一般是不會用到的,所以也不用修改.

第二個標簽

show quick help inspector.這個標簽就是一些快捷幫助信息,基本上就是蘋果API中對某個控件的介紹.

第三個標簽

show the identity inspector.在這個標簽下主要做一些標識.我們最常用的就是其中的Custom Class,用這個標簽來關聯(lián)xib文件與我們自己創(chuàng)建的類文件

第四個標簽

show the attributes inspector.在這個標簽使我們最常用的一個標簽,我們通常會使用它進行控件的屬性設置.比如設置模擬器的一些尺度,顏色等相關的.這個標簽的內容(即可設置的屬性)會因控件的不同而變化的.

第五個標簽

show the size inspector.這個標簽是設置frame的相關,主要與尺寸相關.

第六個標簽

show the connections inspector.這個標簽主要負責xib文件與類的源文件交互,通俗的將就是"連線",在xib中控件的屬性與觸發(fā)的動作,都是可以拖一條線到類的源文件中,用代碼進行下步操作的.這會在接下來進行介紹.

實踐出真知

下面通過項目中常用的控件約束逐一講解xib中autolayout的使用

label

一般約束

第一次使用autolayout,先拖一個label試試

這是xib中autolayout布局的一個label,整個操作如圖。

labeltry.gif

這是用三方約束庫masonry進行約束的,看代碼,剛設置的background和text用純代碼寫都是一一對應的,所以,這里告訴你,xcode的可視化ui布局做的很棒,開發(fā)中一般用到的視圖屬性設置,這里都能找到設置的地方

labelmasonry.png

這是用最古老的純代碼編寫ui布局。看下代碼量,如果寫個稍微復雜點的頁面,是不是會覺得不爽。

labelframe.png

從上面可以看出,純代碼開發(fā)的,就不過多討論了,比較masonry和autolayout布局。用哪個呢?autolayout能快masonry幾條街吧。

最后,針對剛入門的ios開發(fā)來說,建議先masonry,再autolayout。因為autolayout的實現(xiàn),你并不知道oc代碼的具體實現(xiàn),不利于修煉內功。(當然,外包的話,你是沒有那個條件的,只能用autolayout,只為快,只是重復)

內邊距需求

項目中l(wèi)abel一般clear背景展示內容,很少有內景色的,但是如果美工設計需要有內邊距的。但是autolayout中并沒有內邊距設置呀?

labelattributed.png

看到label的attributed類型是不是有點聯(lián)想,我們代碼實現(xiàn)label的行間距,字體顏色等富文本設置的時候,用的就是這里的一些東西。但是沒有找到能設置內邊距的。這種需求可以通過自定義label實現(xiàn),如下:

label文本對齊設置內邊距

實踐后發(fā)現(xiàn),這種方式內容是顯示不全的,這里沒有深究,有搞過的朋友,可以留言交流。。。?

labeledge.png

總結:

uilable 自適應高度,是不帶內邊距的。如果非要實現(xiàn)上面的方案也有,只是內容顯示不全。使用TextView是完美的替代方案。下面會講textview的用法

補充

iOS開發(fā)技巧

如果 nib 或 storyboard 里用了 autoLayout,實際運行順序是先執(zhí)行viewDidLoad再執(zhí)行 autoLayout

定制邊框需求

視圖的邊框需求,在項目中大多會用到,我們一般處理方式為view.layer的操作。但是autolayout如何實現(xiàn)呢?

User Defined Runtime Attritubes:用戶定義運行時屬性

bordertool.png

key path。有些感觸吧,oc的kvc編碼,所以,對象的屬性都可以在這里試試看。只是有些特例無法直接實現(xiàn),如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,手敲的話,很容易手誤,又很難發(fā)現(xiàn)。如果設置錯誤,系統(tǒng)默認為黑色。

textview

高度自適應需求

針對label,textview等需要根據(jù)文本內容,高度自適應,可以使用純代碼計算內容高度,但是大家應該也比較詬病這種方法,諸如開發(fā)麻煩,計算不準等。使用autolayout相對來說有種方案就簡單多了,如下:

textviewadjustheight.gif

看效果:

label2textview.png

這里有兩點注意:

1:設置過后,看上圖,高度約束變虛線了。
2:用過masonry都知道,每條約束都有優(yōu)先級,因為masonry也是封裝的NALyoutConstraint嘛,看下圖,不解釋。

constraintpriority.png
masonrypriority.png

總結:
在內容自適應高度的布局需求中,這種布局方式也是一大利器。請善用

1:有內邊距

2:內容默認吸頂

3:如果需要自適應高度,調整高度自適應,不讓滾動即可實現(xiàn)

需求拓展

在實際開發(fā)中,textview不能一直隨著內容增高,會有一個最大高度。實際開發(fā)中,我們一般使用封裝好的自定義textview。如下,這里講autolayout,不過多的了解如何自定義

自定義textview

iOS 使用UITextView計算高度,一行代碼搞定

button

通過上面label的練習,大部分單個視圖的約束操作都是能應對的。下面以button為實例,了解一下不同狀態(tài)的練習。

button 基本設置:背景色、背景圖片、圖片、標題、圓角、狀態(tài):正常,高亮,選中,等

推薦好文

多操作,熟能生巧,xib中ui布局都是操作性的

imageview

imageview基本操作這里就略了,跟上面一樣,下面說一些進階的:

imageview添加子視圖

沒有實際操作過的,并不覺得這里有什么,坑點就是imageview上沒法添加子視圖。純代碼你也可以實踐試試。下面說下xib如何實現(xiàn):

superimageview.png

imageview 添加子視圖:拖拽uiview視圖,class 改為imageview,由于是UIView,要在.m中設置圖片

總結:
碰到視圖嵌套的需求,如果父視圖不支持嵌套,可以試試這么干。把UIView轉為對應子類型

tableview

終于說到tableview,初入門ios的,感覺tableview有些懵逼吧,什么協(xié)議,代理,數(shù)據(jù)源等一些新名詞,整的費解。不過,這里說的是布局,嘿嘿

展示全部內容

tableview本身是可以滾動的,如果cell不多,想展示全部內容共,讓tableview根據(jù)cell自適應高度。參考下面collectionview部分。demo中有實現(xiàn)。

cell自適應高度

我們實際開發(fā)中共,cell自適應高度一般使用的都是三方庫:

xib布局對應:UITableView-FDTemplateLayoutCell

masonry布局對應:HYBMasonryAutoCellHeight

推薦標哥相關masonry筆記

這里了解一下,不用三方庫,實現(xiàn)cell自適應高度的操作:self-sizing cell,實在不想碼字了,看demo實踐

推薦好文

collectionview

如何顯示collectionview全部內容這里有個論題,可以看看。實踐發(fā)現(xiàn),方案如下:

方案:
collectionView的contentSize.height賦給collectionView的高度約束。

總結:
collectionview作為復雜頁面的子視圖,如果需要展示全部cell,這種方案是比較好的。tableview也同樣道理。如果有需求,就可以這么干。。

1:設置collectionview不可滾動
2:設置collectionview高度約束

注意:獲取contentSize一定要在cell加載完成后,不然獲取到為0。collectionview是不會顯示的,cell也不會加載。

scrollview

開發(fā)中,有些頁面,無規(guī)律沒法用tableview,但是有很長,超過了屏幕,我們首選scrollview。說到scrollview,感覺是比較難用的,特別是masonry布局時候,有些麻煩。這里了解一下,提供一個不錯的方案。

scrollview約束

因為scrollview是可以滾動的,所以有個自身的frame,還有個并不存在的contentview,就是scrollview里的子視圖們。想象一下放映機,放映口和膠片。放映口相當于scrollview的frame,膠片相當于scrollview內的內容。一旦膠片長度超過放映口。是不是就有滑動的觀感了。tableview和collectionview和textview都是繼承自scrollview,所以,都是同樣的道理。如何實現(xiàn)?原理就是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即可。已經(jīng)約束過
2):xposition/width。假設需求是豎直滾動。那么設置水平居中是最合理的。因為scrollview中content的左右相對約束點你都不知道。containerview的左右相對scrollview已經(jīng)約束過,怎樣組合,水平方向才能束縛住containerview不亂跑呢?一是設置containerview的width,二是水平居中,試試看:

scrollviewconstraintsolvehor.gif

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

scrollviewconstraintsolvevertical.gif

scrollview滑動

以上scrollview的約束做過了。知道原理,scrollview以什么樣的frame存在或嵌套都是可以應對的。西面了解下開發(fā)中常見的場景:

subviews超過一屏,使用scrollview實現(xiàn)滾動:

方案:手動計算更新containerview的height約束。

這種虛擬器窗口不能變動


simulatedmetricsinferred.png

調整metrix


simulatedmetricsfreeform.png

調整虛擬窗口,拉大高度


simulatedmetricsscroll.png

手動計算-動態(tài)設置containerview的高度

handlecontainerviewheight.png

控件平分

上面了解了單個視圖的布局。下面了解一下多視圖組合布局的技能。

需求:視圖等寬等間距平分父視圖

viewdivide.gif

比例布局

實際開發(fā)中,比例布局用的感覺不是很多。這里也了解一下:

multiplier.gif
aspectratio.gif

這個第二類約束,解釋一下:

約束過紅view的寬高ratio后,約束報錯,是因為已經(jīng)約束了寬高,現(xiàn)在又約束寬高比1:2,跟原寬高比不一樣,所以,刪除寬高的任一約束即可。

組合批量處理

先看個錯誤的栗子。

grouphandleerror.gif

green的view為啥跑到前面去了呢?而不是4個view左右平分??聪耮reen視圖的相對約束,發(fā)現(xiàn)是相對于safe area。為啥呢 ?因為干剛開始放的時候,green視圖就非常靠上,該視圖左右能看到的第一個視圖就是safe area。所以,就這樣了。這也是autolayout方便的一點,默認視圖依賴,下面看正確的處理。。

grouphandle.gif

解釋一下:組合批量處理分3個步驟:

1:預先處理

上面錯誤例子演示了,首先要把幾個視圖放到差不多的位置,然后再進行整體處理

2:劃整體處理

將視圖組處理為一個整體:

1:水平對齊
2:左右平分,等寬等高

3:整體約束

上面將視圖化為一個整體了,剩下的,就單選任一個視圖,確定yposition/height即可。這里操作的是blue視圖,topSpace和height。這樣就確定了每個視圖的上下左右或寬高四個約束了

組合自適應布局

這種布局的場景一時沒想起來,沒遇到過。這里就不多了解了。推薦一篇好文:

好文推薦

子視圖撐開父視圖

這類布局需求太常見了,總想有騷操作來實現(xiàn),實際開發(fā)中總是用笨方法,計算子視圖高,更新父視圖高。這一點都不智能啊:用autolayout下面有一種方法,一起了解一下:

groupadjust.gif

解釋:

高度自適應

設置約束,記住規(guī)則:先父后子,父無高需自適,子一一約束四方,最后父依賴最底視圖的底部。

圖中的子視圖,由于自適應內容,所以特殊設置了高,priority。上面label,textview有了解過。

priority設置多大?

系統(tǒng)提供的有Required:1000,High:750,Low:250。這里設置時候取值范圍:0 < x < 750 & x != 250。

看效果:

groupadjustresult.gif

基于Object封裝

推薦文章。。這里有了解object的使用。我也是照著實踐的,所以,就不搬磚了

基于UIView封裝

基于UIView封裝xib: xib 還沒那么強大:

1:如果想實現(xiàn)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:如果想實現(xiàn)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統(tǒng)一約束,可以masonry和autolayout共同約束,所以,一般可用在單一約束上,沒有其他customview的相對約束,比如:tableview的headerview。

2):customview.xib不能放到customview中initwithcoder進行初始化加載。只能使用的地方手動加載。因為:customview.xib的file's owner是vc

沒有圖示,是不是看的云里霧里的。真正實踐過的,應該知道我在說什么??吹竭@里的有興趣的朋友,一起交流。

需求完善

上面說了想要封裝customview。并且vc中自動初始化customview,并且vc中能聲明customview中控件的IBOutlet。其實vc和customview能共用customview中控件的交互已經(jīng)很耦合了。還想在vc中開控制customview中的控件,這還要封裝customview干嘛。

上面說封裝customview后,在vc中使用,還要手動加載。費勁,這里引入一個三方庫技術:XXNibBridge

使用很簡單,解決需求也很給力,看demo。

問題:

1、用了XXNibBridge嵌套的view會創(chuàng)建兩次,第一次會被釋放

2、用了XXNibBridge嵌套的view就不能再用Masonry來做約束

3、vc中不能引入customview的控件。會崩潰,沒深入研究

4、loadNibNamed手動加載customview.xib文件,沒有父視圖。

作者很久沒有維護了。如果要使用該庫,項目中各種場景還是要驗證過再用,不然有坑就尷尬了。一般的使用還是完全可以勝任的

xib 高深用法

推薦好文

這里詳細講解了xib一些高端用法,個人了解較少,就不亂說了

IB_DESIGNABLE

IB 動態(tài)渲染。

上文里了解了IB_DESIGNABLE的用法和作用,但是那種方式只是將視圖的處理封裝到自定義視圖中,可以在xib中使用,但是沒法動態(tài)可視化操作。

可視化操作,動態(tài)渲染方案

IBInspectable + IB_DESIGNABLE:可視化動態(tài)渲染視圖

IBInspectable讓支持KVC的屬性能夠在Attribute Inspector中配置
IB_DESIGNABLE的宏的功能就是讓XCode動態(tài)渲染出該類圖形化界面

坑點:
IBInspectable:
布爾類型:BOOL,不能是Boolean
支持類型:屬性支持kvc的:Boolean,Number,String, Localized String, Point, Size, Rect, Range, Color, Image, Nil;

IB_DESIGNABLE:
IBInspectable標記的屬性,要重寫set方法。才能可視化。。。不能重寫get方法。會報錯

這里以cornerRadius實踐:

IBInspectable.png

IBInspectable標記的屬性會在這里顯示,可操作。

IBInspectable1.png

User Defined Runtime Attritubes。這里的值,是你操作IBInspectable標記的屬性后,這里動態(tài)顯示,沒有操作過的,不會顯示。重點是:就算你操作了,也只有IB_DESIGNABLE標記的.m文件重寫了set方法的屬性有效。

IBInspectable2.png

看到?jīng)],這里只操作了radius和color,上面User Defined Runtime Attritubes中就只限是了radius和color

IBDESIGNABLE.gif

總結

一起了解了autolayout的一些基本和進階用法,autolayout操作都是技能性的,多練習就ok了。這里不能窮舉所有的用法,學會自己摸索,根據(jù)約束錯誤提示,練習自己的約束方案和習慣

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容