Auto Layout 和 Constraints
文章修改
2月1日:添加使用約束、編輯約束和iOS特性三個(gè)部分
2月24日:根據(jù)自己的理解,修改iOS特性部分的內(nèi)容
自動(dòng)布局Auto Layout
Auto Layout,通過(guò)設(shè)置在View上的約束,動(dòng)態(tài)計(jì)算視圖層次結(jié)構(gòu)中所有的View的尺寸和位置。舉個(gè)栗子,你約束一個(gè)Button,令它的水平中心線和一個(gè)ImageView相同,并且它的上邊緣距離ImageView的下邊緣有8個(gè)像素。如果ImageView的尺寸或者位置改變,Button會(huì)自動(dòng)調(diào)整,以符合之前設(shè)置的約束。
基于約束的Auto Layout,使我們搭建能夠動(dòng)態(tài)響應(yīng)內(nèi)部和外部變化的用戶界面。
外部變化
外部變化發(fā)生于superview的尺寸或者位置改變,比如,
設(shè)備屏幕旋轉(zhuǎn);
支持不同屏幕大小的設(shè)備。
這時(shí),所有的View都要重新計(jì)算尺寸和位置。每一次變化,都會(huì)刷新視圖層級(jí)結(jié)構(gòu)的布局。這些變化大部分發(fā)生在運(yùn)行時(shí),它們需要APP能夠動(dòng)態(tài)響應(yīng)。
內(nèi)部變化
內(nèi)部變化發(fā)生于你的界面中的View的尺寸或者位置發(fā)生改變。比如,
APP中顯示的內(nèi)容的改變,新的內(nèi)容可能需要一個(gè)新的布局。一般,在顯示文字或者圖片時(shí)會(huì)出現(xiàn)這種情況;
APP支持動(dòng)態(tài)設(shè)置。如果用戶可以設(shè)置字體大小,這將會(huì)改變?nèi)魏闻c文本相關(guān)的控件的高度或者寬度,布局必須能夠適應(yīng)變化。
約束Constraints
Auto Layout的實(shí)現(xiàn)是基于設(shè)置在View上的一系列約束的。每一條約束都是一個(gè)表達(dá)式。
下圖是官方文檔給的示例圖:
這個(gè)約束表明,Red View的左邊緣與Blud View的右邊緣的距離為8。這個(gè)等式由以下幾個(gè)部分組成:
Item 1 :表達(dá)式中的第一個(gè)控件。在這個(gè)例子中是Red View;
Attribute 1 :第一個(gè)控件的一個(gè)屬性。在這個(gè)例子中是Red View的leading edge;
Relationship :表達(dá)式左右兩邊的關(guān)系,可以使等于,大于等于或者小于等于。在這個(gè)例子中,兩邊的關(guān)系是相等的;
Multiplier :和第二個(gè)控件的屬性相乘的乘數(shù),是一個(gè)浮點(diǎn)型。在這個(gè)例子中是1.0。一般情況下,這個(gè)值不可置為0.0;
Item 2 :表達(dá)式中的第二個(gè)控件。在這個(gè)例子中是Blue View。它是可以為空的,即表達(dá)式變成Item 1 * Attribute 1 = 0.0 * NotAnAttribute + Constant;
Attribute 2 :第二個(gè)控件的一個(gè)屬性。在這個(gè)例子中是Blue View的trailing edge;
Constant :一個(gè)浮點(diǎn)型的常數(shù)。在這個(gè)例子中是8.0。
大部分的約束是定義兩個(gè)控件之間的關(guān)系。這些控件必須是View或者是Layout Guide。約束也可以定義一個(gè)控件的兩個(gè)屬性之間的關(guān)系,比如設(shè)置一個(gè)控件的上邊緣到下邊緣的距離、左邊緣到右邊緣的距離,即它的高度或者寬度。當(dāng)表達(dá)式中的Item 2為空時(shí),它的屬性必須被設(shè)為Not An Attribute,并且Multiplier置為0.0。
約束中用到的屬性
通常情況下,包含四個(gè)邊(leading,trailing,top和bottom),以及高度(height),寬度(width),水平中心點(diǎn)(CenterY),垂直中心點(diǎn)(CenterX)。文本類型的控件還有一個(gè)基線(baseline)屬性。
屬性說(shuō)明
Height和Width。這兩個(gè)屬性可以被直接賦值,可以是一個(gè)常數(shù),也可以是其他View的Height或者Width值。但是,不可以為負(fù)數(shù)。
Top、Bottom、Baseline。可以和Top、Bottom、Baseline、CenterY組合。
Leading和Trailing。可以和Leading、Trailing、CenterX組合。
Left和Right。避免使用這兩個(gè)屬性,而使用Leading和Trailing來(lái)替代它們。
CenterX和CenterY。CenterX可以和Leading、Trailing、Left、Right組合。CenterY可以和Top、Bottom、Baseline組合。
使用屬性定義約束
上面提到的屬性可以分為兩類,尺寸相關(guān)和位置相關(guān)。尺寸相關(guān)(如height、width)用來(lái)定義物件的大小。位置相關(guān)(如leading,top)的屬性用來(lái)表明該物件和其他物件之間的位置關(guān)系。使用這些屬性時(shí)需要注意:
不要使用尺寸相關(guān)的屬性去約束一個(gè)位置相關(guān)的屬性。
只可以給尺寸相關(guān)的屬性直接賦值一個(gè)常量。
舉幾個(gè)簡(jiǎn)單的例子:
// 設(shè)置一個(gè)固定高度 View.height = 0.0 * NotAnAttribute + 40.0// 設(shè)置兩個(gè)按鈕之間的固定距離 Button_2.leading = 1.0 * Button_1.trailing + 8.0// 讓兩個(gè)按鈕的左邊緣對(duì)齊 Button_1.leading = 1.0 * Button_2.leading + 0.0// 給兩個(gè)按鈕相同的寬度 Button_1.width = 1.0 * Button_2.width + 0.0// 讓View的中心和父類的中心相同 View.centerX = 1.0 * Superview.centerX + 0.0 View.centerY = 1.0 * Superview.centerY + 0.0// 設(shè)置一個(gè)View的寬高比 View.height = 2.0 * View.width + 0.0約束的設(shè)置沒(méi)有最好的,只有最適合的。
約束的優(yōu)先級(jí)
優(yōu)先級(jí)priority是Auto Layout在計(jì)算的時(shí)候用到的參數(shù)。優(yōu)先級(jí)的值可以是1-1000任意整數(shù)。系統(tǒng)定義了low(250)、medium(500)、high(750)和required(1000)四個(gè)等級(jí)。一般情況下,我們手動(dòng)設(shè)置的優(yōu)先級(jí)的值也會(huì)集中在這四個(gè)等級(jí)下。
優(yōu)先級(jí)默認(rèn)值是1000。
關(guān)于Auto Layout是如何通過(guò)優(yōu)先級(jí)來(lái)計(jì)算出解決方案,我在看完官方文檔后還是一頭霧水。希望有大神可以指點(diǎn)一二。
使用約束
添加約束
在storyboard中有3種方式添加約束。
在View之間使用Control-Drag;
使用Pin和Align工具;
讓Interface Builder自動(dòng)添加約束。
Control-Dragging
所謂Control-Draging就是按住Control鍵,用鼠標(biāo)左鍵拖動(dòng)的方式添加約束。這兩步操作也可以用按住鼠標(biāo)右鍵拖動(dòng)來(lái)替代。
當(dāng)釋放鼠標(biāo)左鍵后,就會(huì)彈出一個(gè)HUD,顯示可以設(shè)置的約束。
Interface Builder會(huì)根據(jù)選擇的兩個(gè)控件以及拖動(dòng)的方向篩選出可以設(shè)置的約束。如果拖動(dòng)的方向傾向于水平,你可以選擇設(shè)置水平方向上的間距和垂直方向上的對(duì)齊方式。反之,如果拖動(dòng)的方向傾向與垂直,則可以選擇設(shè)置垂直方向上的間距和水平方向上的對(duì)齊方式。
提示:
可以從一個(gè)控件拖動(dòng)到另一個(gè)控件,設(shè)置它們之間的關(guān)系。也可以拖動(dòng)到控件自身,設(shè)置寬度和高度;
不僅可以在Scene中直接拖動(dòng),可以在Storyboard左側(cè)的視圖大綱中用同樣的方式拖動(dòng)。在大綱中拖動(dòng)設(shè)置約束,會(huì)顯示出所有的可選約束,而不會(huì)進(jìn)行篩選;
Control-Dragging可以非常快速得設(shè)置約束。這些約束是基于Scene中View的當(dāng)前的位置,因此在設(shè)置約束之前要定位好View。
使用Stack、Align、Pin、Resolve工具
Interface Builder在Storyboard的編輯窗口的右下角提供四個(gè)自動(dòng)布局的工具,分別是Stack、Align、Pin、Resolve Auto Layout Issues。
當(dāng)你想精確控制約束的Constant或者想一次性添加多個(gè)約束,可以使用Align和Pin工具。使用Align和Pin還有一個(gè)好處,我們不是必須要設(shè)置好View的位置,而是只需要定好相對(duì)位置,添加約束,然后update frames。Auto Layout會(huì)自動(dòng)計(jì)算出正確的位置。
Stack Tools
Stack Tools可以將選中的一個(gè)或者多個(gè)控件嵌入到一個(gè)Stack View中,并會(huì)重新計(jì)算布局。Stack View是iOS 9添加的新特性。
對(duì)于Stack View,我還沒(méi)有弄明白使用方法,所以這里不講述。
Align Tool
Align Tools可以快速對(duì)齊控件。選擇一個(gè)或多個(gè)你想對(duì)齊的控件,然后單擊Align Tool。然后會(huì)彈出可選的一系列對(duì)齊方式。
選擇其中的選項(xiàng),然后點(diǎn)擊Add Constraints。之后,就會(huì)自動(dòng)添加對(duì)齊的約束設(shè)置。大部分情況下,會(huì)選擇兩個(gè)或者兩個(gè)以上的View來(lái)設(shè)置對(duì)齊。Horizonally in Container和Vertically in Container這兩個(gè)可以添加到單一的View上。
Pin Tool
Pin是大頭針的意思。所以這個(gè)工具可以用來(lái)給View定位。它可以讓我們快速設(shè)置一個(gè)View相對(duì)于它周邊View位置或者它的寬高。選擇一個(gè)你想對(duì)其進(jìn)行定位的View,單擊Pin Tool,會(huì)彈出如下的窗口。
窗口的上半部分,可以設(shè)置選中的View的Top,Bottom,Leading,Trailing與相鄰最近的View的間距。最初顯示的數(shù)字是當(dāng)前的間距。我們可以輸入一個(gè)自定義的值,還可以點(diǎn)擊輸入框右邊的倒三角,在彈出的下拉菜單中選擇參照的View。關(guān)于Constrain to margins選項(xiàng),如果選中,會(huì)將父視圖的外邊距作為間距的值的參考。
下半部分可以設(shè)置寬高相關(guān)的屬性。寬和高默認(rèn)的是Scene中的尺寸,也可以自定義值。寬高比的默認(rèn)值也是根據(jù)Scene中的尺寸進(jìn)行計(jì)算。如果想自定義的話,只有在設(shè)置完寬高比之后修改這個(gè)約束。
一般情況下,選擇一個(gè)View,對(duì)它進(jìn)行定位。選擇兩個(gè)及其以上的View設(shè)置Equal Height或者Equal Width。使用Pin Tool設(shè)置完約束后,可能需要Update frames。
Resolve Auto Layout Issues
Resolve Auto Layout Issues提供一些解決Auto Layout問(wèn)題的方法。上半部分只針對(duì)選中的View,下半部分則針對(duì)Scene中所有的View。
我們可以
根據(jù)當(dāng)前約束更改frame;
根據(jù)當(dāng)前frame更改約束的設(shè)置;
添加缺少的約束;
清除已添加的約束;
設(shè)置系統(tǒng)推薦的約束。
這些功能字面上寫(xiě)得很清楚,具體的效果大家可以用簡(jiǎn)單的Demo來(lái)看一下。
讓Interface Builder為我們?cè)O(shè)置約束
Interface Builder可以為我們創(chuàng)建部分或者全部的約束。根據(jù)所提供的View的尺寸和位置,它會(huì)推斷出最好的約束。前提是,我們必須確定View的位置并不再更改。一個(gè)小小的間距的改變,可能對(duì)于整個(gè)布局來(lái)說(shuō)確實(shí)巨大的。
如果想讓Interface Builder來(lái)完成約束的添加,單擊上文提到的Resolve Auoto Layout Issues工具,點(diǎn)擊Reset to Suggested Constraints。Interface Builder就會(huì)為已選的(也可以是Scene中全部的)View創(chuàng)建合適的約束。
另外,我們可以自己添加一部分約束,然后選擇Add missing Constraints,讓Interface Builder來(lái)添加剩下的所需的約束。
這種方法可以快速完成約束的設(shè)置。但是,有可能運(yùn)行得到的UI并不是你想要的。要不斷地測(cè)試UI,修改約束,以達(dá)到最終想要的效果。
編輯約束
添加約束之后,需要能夠找到它、查看它、編輯它。
在Scene里查看約束
編輯窗口會(huì)顯示作用于當(dāng)前選擇的View的約束。通過(guò)線的形狀、顏色和類型說(shuō)明當(dāng)前約束的當(dāng)前狀態(tài)。
I-bars(兩端是T型的線):I-bars顯示間距的大小。可能是兩個(gè)控件之間的大小,可能是一個(gè)控件的高度或者寬度。
Plain Line(一條普通的直線):Plain Line顯示控件邊緣的對(duì)齊方式。例如,兩個(gè)或兩個(gè)以后的控件是左對(duì)齊的,那么,這條線會(huì)連接著這些控件,并且與它們的Leading之間的間距為0。
Solid Line:實(shí)線表示這個(gè)約束是Required,即priority == 1000。
Dash Line:虛線表示這個(gè)約束是Optional,即priority < 1000。
Red Line:紅線表示被約束的影響的控件的約束設(shè)置有錯(cuò)誤。具體的原因可以點(diǎn)擊大綱中每個(gè)Scene的右邊的箭頭查看。這時(shí),箭頭是紅色的。
Orange Line:橘黃色的線表明,Auto Layout根據(jù)已有約束計(jì)算出來(lái)的frame和當(dāng)前Scene中設(shè)置的frame不同。這時(shí),大綱中的右邊的箭頭是黃色的。可以用Resolve Auot Layout Issues -> Update frames來(lái)進(jìn)行修正位置。
Blue Line:藍(lán)色的線表示當(dāng)前的約束設(shè)置是正確的,并且控件的位置和Auto Layout計(jì)算出來(lái)的位置是一樣的。
Equal Badges:相等標(biāo)記表明兩個(gè)控件的寬度或者高度是相同的。并且標(biāo)記中包含=符號(hào)。
Greater-than-or-equal and less-than-or-equal badges:和Equal Badges類似,它們是標(biāo)記約束的關(guān)系是大于等于或者小于等于的,同時(shí)也會(huì)顯示對(duì)應(yīng)的符號(hào)。
快速找到并編輯添加的約束
所有添加的約束都陳列在大綱里。這些約束以偽代碼的形式呈現(xiàn)。當(dāng)選擇一個(gè)約束時(shí),會(huì)在Scene中高亮顯示,可以幫助我們快速找到它。而且,可以在右側(cè)的Show the Size inspector下對(duì)約束的Constant、Priority、Multiplier、Relation、Identifier、Placeholder屬性進(jìn)行編輯。
一旦UI變得復(fù)雜之后,我們用這種方式找約束就會(huì)顯得很吃力。Show the Size inspector工具可以顯示出在當(dāng)前選中的控件上添加的約束。約束的一部分屬性也可以在這里進(jìn)行修改。
注意:這里雖然都是在Show the Size Inspector下進(jìn)行修改,但是前者是選擇一個(gè)約束,后者是選擇一個(gè)控件。
Auto Layout在iOS中的特性
iOS在與Auto Layout方面有一些獨(dú)有的特性,包括top and bottom layout guides、Layout Margins。
Top and Bottom Layout Guides
top and bottom layout guides表示當(dāng)前的ViewController從最上面到最下面的可見(jiàn)范圍。如果不希望顯示的內(nèi)容在UIKit bars(例如status bar,navigation bar,tab bar)下面,那么就可以和上下的layout guide來(lái)設(shè)置約束。
layout guides是遵守UILayoutSupport協(xié)議的。這個(gè)協(xié)議有一個(gè)length屬性,來(lái)表示guide和root view(root view就是ViewController默認(rèn)添加的view)邊緣的距離:
對(duì)于top layout guide,length指明ViewController的root view的上邊緣和覆蓋在root view上的bar(例如status bar和navigation bar)的底部的距離。
對(duì)于bottom layout guide,length指明ViewController的root view的下邊緣和覆蓋在root view上的bar(例如tab bar)的頂部的距離。
在iOS 9中,guide也可以像控件一樣,支持用top、bottom、height設(shè)置約束。比如,用top layout guide的bottom屬性和bottom layout guide的top屬性與view設(shè)置約束。UILayoutSupport還提供了topAnchor、bottomAnchor、heightAnchor屬性,可以讓我們用代碼的形式來(lái)設(shè)置約束。
如果layout guides是view最近的"控件",那么系統(tǒng)會(huì)自動(dòng)將layout guides作為設(shè)置約束的對(duì)象。當(dāng)使用Pin Tool時(shí),可以在layout guides和root view的上下邊緣進(jìn)行選擇。
Layout margins
Auto Layout為每一個(gè)view都定義了margin。margin指的是控件顯示內(nèi)容部分的邊緣和控件邊緣的距離。就像“回”這個(gè)漢字一樣,外面的“口”就是控件的外邊緣,里面的“口”是控件顯示內(nèi)容的部分的邊緣,我暫且稱它為內(nèi)邊緣,這兩個(gè)邊緣之間的距離就是margin。
可以用layoutMargins或者layoutMarginsGuide屬性獲得view的margin。layoutMargins允許獲取或者設(shè)置UIEdgeInsets結(jié)構(gòu)的margin,layoutMarginsGuide則只會(huì)獲取到只讀的UILayoutGuide對(duì)象。
每一個(gè)view的默認(rèn)的margin是8。可以根據(jù)APP的需要進(jìn)行修改。__系統(tǒng)給ViewController的root View設(shè)置的margin則不能修改__。root View的上下的margin為0,左右的margin為20。
當(dāng)使用Control-Dragging方式,給一個(gè)View和它們父視圖設(shè)置約束時(shí),默認(rèn)使用內(nèi)邊緣,而不是外邊緣。當(dāng)使用Pin Tool時(shí),如果Constraint to margins被勾選,則會(huì)使用父視圖的內(nèi)邊緣作為設(shè)置約束的參照,如果沒(méi)有被勾選,則使用外邊緣作為設(shè)置約束的參照。所見(jiàn)到的效果就是兩種參照下的約束的Constant的值相差一個(gè)margin。
當(dāng)在Interface Builder中編輯約束時(shí),First Item和Second Item的彈出菜單中可以選擇Relative to margin。如果勾選,會(huì)在top、leading等屬性后面加上Margin,變成topMargin、leadingMargin等,意味著約束的設(shè)置參照View的內(nèi)邊緣而不是外邊緣。
設(shè)置約束時(shí)的一些建議
官方文檔為我們提供了一些設(shè)置約束時(shí)的建議
__在最相近的兩個(gè)控件之間創(chuàng)建約束。__假如有3個(gè)Button,分別是first,second,third。可以約束first的右邊緣和second的左邊緣的距離,second的右邊緣和third的左邊緣的距離。而不要約束first的右邊緣和third的左邊緣距離,中間隔了一個(gè)second。
__避免設(shè)置固定的高度和寬度。__Auto Layout是動(dòng)態(tài)響應(yīng)布局的變化。一個(gè)控件如果設(shè)置固定尺寸,那么就會(huì)失去自動(dòng)調(diào)整的能力。
在update frames時(shí)注意,如果這個(gè)控件沒(méi)有設(shè)置足夠多的約束來(lái)確定它的位置和尺寸,那么可能會(huì)導(dǎo)致一些意想不到的后果。比如會(huì)跑到屏幕外,或者因?yàn)閷挾取⒏叨葹?而消失等等。
給所有的控件取一個(gè)有意義的名稱。在使用工具時(shí),可以方便地辨別出View。
使用leading和trailing代替left和right。
當(dāng)使用代碼創(chuàng)建約束時(shí),確保將它們的translatesAutoresizingMaskIntoConstraints屬性為NO。默認(rèn)情況下,系統(tǒng)會(huì)基于控件的frame自動(dòng)創(chuàng)建一系列約束。當(dāng)你添加自己的約束時(shí),不可避免地會(huì)和自動(dòng)生成的約束產(chǎn)生沖突。
參考內(nèi)容:
蘋(píng)果官方文檔:Auto Layout Guide
本文中的圖片均取自蘋(píng)果官方文檔
總結(jié)
以上是生活随笔為你收集整理的Auto Layout 和 Constraints的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梦到同班异性是为什么
- 下一篇: 做梦梦到棺材压到自己是什么意思