Auto Layout 和 Constraints

文章修改
2月1日:添加使用約束編輯約束iOS特性三個部分
2月24日:根據本身的理解,修改iOS特性部分的內容html

自動佈局Auto Layout

Auto Layout,經過設置在View上的約束,動態計算視圖層次結構中全部的View的尺寸和位置。舉個栗子,你約束一個Button,令它的水平中心線和一個ImageView相同,而且它的上邊緣距離ImageView的下邊緣有8個像素。若是ImageView的尺寸或者位置改變,Button會自動調整,以符合以前設置的約束。 ios

基於約束的Auto Layout,使咱們搭建可以動態響應內部和外部變化的用戶界面。app

外部變化

外部變化發生於superview的尺寸或者位置改變,好比,less

  • 設備屏幕旋轉;ide

  • 支持不一樣屏幕大小的設備。工具

這時,全部的View都要從新計算尺寸和位置。每一次變化,都會刷新視圖層級結構的佈局。這些變化大部分發生在運行時,它們須要APP可以動態響應。佈局

內部變化

內部變化發生於你的界面中的View的尺寸或者位置發生改變。好比,測試

  • APP中顯示的內容的改變,新的內容可能須要一個新的佈局。通常,在顯示文字或者圖片時會出現這種狀況;字體

  • APP支持動態設置。若是用戶能夠設置字體大小,這將會改變任何與文本相關的控件的高度或者寬度,佈局必須可以適應變化。ui

約束Constraints

Auto Layout的實現是基於設置在View上的一系列約束的。每一條約束都是一個表達式。

下圖是官方文檔給的示例圖:

蘋果官方文檔的示例圖

這個約束代表,Red View左邊緣Blud View右邊緣的距離爲8。這個等式由如下幾個部分組成:

  • Item 1 :表達式中的第一個控件。在這個例子中是Red View

  • Attribute 1 :第一個控件的一個屬性。在這個例子中是Red Viewleading edge

  • Relationship :表達式左右兩邊的關係,可使等於大於等於或者小於等於。在這個例子中,兩邊的關係是相等的;

  • Multiplier :和第二個控件的屬性相乘的乘數,是一個浮點型。在這個例子中是1.0。通常狀況下,這個值不可置爲0.0

  • Item 2 :表達式中的第二個控件。在這個例子中是Blue View。它是能夠爲空的,即表達式變成Item 1 * Attribute 1 = 0.0 * NotAnAttribute + Constant

  • Attribute 2 :第二個控件的一個屬性。在這個例子中是Blue Viewtrailing edge

  • Constant :一個浮點型的常數。在這個例子中是8.0

大部分的約束是定義兩個控件之間的關係。這些控件必須是View或者是Layout Guide。約束也能夠定義一個控件的兩個屬性之間的關係,好比設置一個控件的上邊緣到下邊緣的距離、左邊緣到右邊緣的距離,即它的高度或者寬度。當表達式中的Item 2爲空時,它的屬性必須被設爲Not An Attribute,而且Multiplier置爲0.0

約束中用到的屬性

一般狀況下,包含四個邊(leading,trailing,top和bottom),以及高度(height),寬度(width),水平中心點(CenterY),垂直中心點(CenterX)。文本類型的控件還有一個基線(baseline)屬性。

image

屬性說明

  • Height和Width。這兩個屬性能夠被直接賦值,能夠是一個常數,也能夠是其餘View的Height或者Width值。可是,不能夠爲負數。

  • Top、Bottom、Baseline。能夠和TopBottomBaselineCenterY組合。

  • Leading和Trailing。能夠和LeadingTrailingCenterX組合。

  • Left和Right。避免使用這兩個屬性,而使用LeadingTrailing來替代它們。

  • CenterX和CenterY。CenterX能夠和LeadingTrailingLeftRight組合。CenterY能夠和TopBottomBaseline組合。

使用屬性定義約束

上面提到的屬性能夠分爲兩類,尺寸相關位置相關。尺寸相關(如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

  • 使用PinAlign工具;

  • 讓Interface Builder自動添加約束。

Control-Dragging

所謂Control-Draging就是按住Control鍵,用鼠標左鍵拖動的方式添加約束。這兩步操做也能夠用按住鼠標右鍵拖動來替代。

image

當釋放鼠標左鍵後,就會彈出一個HUD,顯示能夠設置的約束。

image

Interface Builder會根據選擇的兩個控件以及拖動的方向篩選出能夠設置的約束。若是拖動的方向傾向於水平,你能夠選擇設置水平方向上的間距和垂直方向上的對齊方式。反之,若是拖動的方向傾向與垂直,則能夠選擇設置垂直方向上的間距和水平方向上的對齊方式。

提示:

  1. 能夠從一個控件拖動到另外一個控件,設置它們之間的關係。也能夠拖動到控件自身,設置寬度和高度;

  2. 不只能夠在Scene中直接拖動,能夠在Storyboard左側的視圖大綱中用一樣的方式拖動。在大綱中拖動設置約束,會顯示出全部的可選約束,而不會進行篩選;

  3. Control-Dragging能夠很是快速得設置約束。這些約束是基於Scene中View的當前的位置,所以在設置約束以前要定位好View。

使用Stack、Align、Pin、Resolve工具

Interface Builder在Storyboard的編輯窗口的右下角提供四個自動佈局的工具,分別是StackAlignPinResolve Auto Layout Issues

image

當你想精確控制約束的Constant或者想一次性添加多個約束,可使用Align和Pin工具。使用Align和Pin還有一個好處,咱們不是必需要設置好View的位置,而是隻須要定好相對位置,添加約束,而後update frames。Auto Layout會自動計算出正確的位置。

Stack Tools

Stack Tools能夠將選中的一個或者多個控件嵌入到一個Stack View中,並會從新計算佈局。Stack View是iOS 9添加的新特性。

對於Stack View,我尚未弄明白使用方法,因此這裏不講述。

Align Tool

Align Tools能夠快速對齊控件。選擇一個或多個你想對齊的控件,而後單擊Align Tool。而後會彈出可選的一系列對齊方式。

image

選擇其中的選項,而後點擊Add Constraints。以後,就會自動添加對齊的約束設置。大部分狀況下,會選擇兩個或者兩個以上的View來設置對齊。Horizonally in ContainerVertically in Container這兩個能夠添加到單一的View上。

Pin Tool

Pin是大頭針的意思。因此這個工具能夠用來給View定位。它可讓咱們快速設置一個View相對於它周邊View位置或者它的寬高。選擇一個你想對其進行定位的View,單擊Pin Tool,會彈出以下的窗口。

image

窗口的上半部分,能夠設置選中的View的Top,Bottom,Leading,Trailing與相鄰最近的View的間距。最初顯示的數字是當前的間距。咱們能夠輸入一個自定義的值,還能夠點擊輸入框右邊的倒三角,在彈出的下拉菜單中選擇參照的View。關於Constrain to margins選項,若是選中,會將父視圖的外邊距做爲間距的值的參考。

image

下半部分能夠設置寬高相關的屬性。寬和高默認的是Scene中的尺寸,也能夠自定義值。寬高比的默認值也是根據Scene中的尺寸進行計算。若是想自定義的話,只有在設置完寬高比以後修改這個約束。

通常狀況下,選擇一個View,對它進行定位。選擇兩個及其以上的View設置Equal Height或者Equal Width。使用Pin Tool設置完約束後,可能須要Update frames

Resolve Auto Layout Issues

Resolve Auto Layout Issues提供一些解決Auto Layout問題的方法。上半部分只針對選中的View,下半部分則針對Scene中全部的View。

image

咱們能夠

  • 根據當前約束更改frame;

  • 根據當前frame更改約束的設置;

  • 添加缺乏的約束;

  • 清除已添加的約束;

  • 設置系統推薦的約束。

這些功能字面上寫得很清楚,具體的效果你們能夠用簡單的Demo來看一下。

讓Interface Builder爲咱們設置約束

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,修改約束,以達到最終想要的效果。

編輯約束

添加約束以後,須要可以找到它、查看它、編輯它。

在Scene裏查看約束

編輯窗口會顯示做用於當前選擇的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下對約束的ConstantPriorityMultiplierRelationIdentifierPlaceholder屬性進行編輯。

image

一旦UI變得複雜以後,咱們用這種方式找約束就會顯得很吃力。Show the Size inspector工具能夠顯示出在當前選中的控件上添加的約束。約束的一部分屬性也能夠在這裏進行修改。

image

注意:這裏雖然都是在Show the Size Inspector下進行修改,可是前者是選擇一個約束,後者是選擇一個控件。

Auto Layout在iOS中的特性

iOS在與Auto Layout方面有一些獨有的特性,包括top and bottom layout guidesLayout Margins

Top and Bottom Layout Guides

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也能夠像控件同樣,支持用topbottomheight設置約束。好比,用top layout guide的bottom屬性和bottom layout guide的top屬性與view設置約束。UILayoutSupport還提供了topAnchorbottomAnchorheightAnchor屬性,可讓咱們用代碼的形式來設置約束。

若是layout guides是view最近的"控件",那麼系統會自動將layout guides做爲設置約束的對象。當使用Pin Tool時,能夠在layout guides和root view的上下邊緣進行選擇。

Layout margins

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 ItemSecond Item的彈出菜單中能夠選擇Relative to margin。若是勾選,會在topleading等屬性後面加上Margin,變成topMarginleadingMargin等,意味着約束的設置參照View的內邊緣而不是外邊緣。

設置約束時的一些建議

官方文檔爲咱們提供了一些設置約束時的建議

  • __在最相近的兩個控件之間建立約束。__假若有3個Button,分別是first,second,third。能夠約束first的右邊緣和second的左邊緣的距離,second的右邊緣和third的左邊緣的距離。而不要約束first的右邊緣和third的左邊緣距離,中間隔了一個second。

  • __避免設置固定的高度和寬度。__Auto Layout是動態響應佈局的變化。一個控件若是設置固定尺寸,那麼就會失去自動調整的能力。

  • update frames時注意,若是這個控件沒有設置足夠多的約束來肯定它的位置和尺寸,那麼可能會致使一些意想不到的後果。好比會跑到屏幕外,或者由於寬度、高度爲0而消失等等。

  • 給全部的控件取一個有意義的名稱。在使用工具時,能夠方便地辨別出View。

  • 使用leadingtrailing代替leftright

  • 當使用代碼建立約束時,確保將它們的translatesAutoresizingMaskIntoConstraints屬性爲NO。默認狀況下,系統會基於控件的frame自動建立一系列約束。當你添加本身的約束時,不可避免地會和自動生成的約束產生衝突。

參考內容:
蘋果官方文檔:Auto Layout Guide

本文中的圖片均取自蘋果官方文檔

相關文章
相關標籤/搜索