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