[TOC=2]
視圖層次結構的布局被定義為一系列線性方程式。 每個約束表示一個單一的等式。 你的目標是聲明一系列只有一個答案的方程。
示例公式如下所示。

這個約束表明紅色視圖的前沿必須在藍色視圖的后沿后有8.0個點。 它的等式有很多部分:
* Item 1. 等式中的第一項 ,此處指紅色視圖。 該項目必須是視圖或 layout guide 。
* Attribute 1. 屬性將被限制在第一項上 - 此處指紅色視圖的前邊。
* Relationship. 左側和右側之間的關系。 關系可以具有三個值之一:等于,大于或等于或小于或等于。 此處,左側和右側相等。
* Multiplier.屬性2的值乘以該浮點數。 此處,乘數是1.0。
* Item 2. 等式中的第二項,此處指藍色視圖。 與第一項不同,這可以留空。
* Attribute 2.要在第二個項目上約束的屬性 ,在本例中為藍色視圖的后邊緣。 如果第二項留空,則此項必須為非屬性。
* Constant. 一個常量,浮點偏移量 - 在這種情況下,8.0。 該值將被添加到屬性2的值中。
大多數約束定義了用戶界面中兩個項目之間的關系。 這些項目可以是視圖或 layout guides 。 約束還可以定義單個項目的兩個不同屬性之間的關系,例如,設置項目高度和寬度之間的寬高比。 也可以為項目的高度或寬度分配常數值。 當使用常量值時,第二項留空,第二項屬性設置為非屬性,乘數設置為 0.0。
## 自動布局屬性
在自動布局中,這些屬性定義了一個可以被約束的特征。 一般來說,這包括四個邊(頭部,尾部,頂部和底部)以及高度,寬度以及垂直和水平中心。 文本項目還有一個或多個基準屬性。

有關完整的屬性列表,請參閱 [NSLayoutAttribute](https://developer.apple.com/documentation/appkit/nslayoutattribute) 枚舉。
> 注意
> 盡管 OS X 和 iOS 都使用 NSLayoutAttribute 枚舉,但它們定義了稍微不同的一組值。 要查看完整的屬性列表,請確保正在查看正確的平臺文檔。
## 樣本方程
這些公式可用的各種參數和屬性可以讓您創建許多不同類型的約束條件。 可以定義視圖之間的空間,對齊視圖的邊緣,定義兩個視圖的相對大小,甚至可以定義視圖的寬高比。 但是,并非所有屬性都是兼容的。
有兩種基本類型的屬性。 大小屬性(例如,高度和寬度)和位置屬性(例如Leading,Left和Top)。 大小屬性用于指定項目的大小,而不指示其位置。 位置屬性用于指定項目相對于其他項目的位置。 但是,它們沒有指示物品的大小。
考慮到這些差異,以下規則適用:
* 無法將大小屬性限制為位置屬性。
* 不能將常量值分配給位置屬性。
* 不能使用具有位置屬性的非唯一性乘數(1.0以外的值)。
* 對于位置屬性,不能將垂直屬性約束為水平屬性。
* 對于位置屬性,無法將 Leading 或 Trailing 屬性限制為 Left 或 Right 屬性。
例如,將項目的 Top 設置為常量值 20.0 在沒有附加上下文的情況下沒有意義。 必須始終相對于其他項目定義項目的位置屬性,例如,在父視圖頂部下方 20.0 個點。 但是,將項目的高度設置為 20.0 是完全有效的。 有關更多信息,請參閱 [Interpreting Values](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW22 )。
清單 3-1 顯示了各種常見約束的示例方程。
> 注意
> 本章中的所有示例方程都是使用偽代碼呈現的。 要使用實際代碼查看實際約束,請參閱[Programmatically Creating Constraints](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1) 或者 [Auto Layout Cookbook](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html#//apple_ref/doc/uid/TP40010853-CH3-SW1)。
###### 清單3-1常見約束的示例方程
~~~
// Setting a constant height
View.height = 0.0 * NotAnAttribute + 40.0
// Setting a fixed distance between two buttons
Button_2.leading = 1.0 * Button_1.trailing + 8.0
// Aligning the leading edge of two buttons
Button_1.leading = 1.0 * Button_2.leading + 0.0
// Give two buttons the same width
Button_1.width = 1.0 * Button_2.width + 0.0
// Center a view in its superview
View.centerX = 1.0 * Superview.centerX + 0.0
View.centerY = 1.0 * Superview.centerY + 0.0
// Give a view a constant aspect ratio
View.height = 2.0 * View.width + 0.0
~~~
## 相等,不賦值
注意到 Note 中顯示的公式表示等式而非賦值是很重要的。
當自動布局解決這些方程時,它不會將右側的值分配給左側。 相反,它會計算屬性1和屬性2的值,使關系成為真。 這意味著我們可以經常自由地重新排列方程中的項目。 例如,清單3-2中的公式與Note中的對應項相同。
###### 清單3-2倒置的方程
~~~
// Setting a fixed distance between two buttons
Button_1.trailing = 1.0 * Button_2.leading - 8.0
// Aligning the leading edge of two buttons
Button_2.leading = 1.0 * Button_1.leading + 0.0
// Give two buttons the same width
Button_2.width = 1.0 * Button.width + 0.0
// Center a view in its superview
Superview.centerX = 1.0 * View.centerX + 0.0
Superview.centerY = 1.0 * View.centerY + 0.0
// Give a view a constant aspect ratio
View.width = 0.5 * View.height + 0.0
~~~
> 注意
> 重新排序項目時,確保你反轉乘數和常數。 例如,8.0 的常數變為 -8.0。 2.0 的乘數變為0.5 。 0.0 的常量和 1.0 的乘數保持不變。
你會發現 Auto Layout 經常提供多種方法來解決同樣的問題。 理想情況下,您應該選擇最清楚描述您的意圖的解決方案。 但是,不同的開發人員無疑會不同意哪種解決方案是最好的。 在這里,保持一致勝于正確。 如果您選擇一種方法并始終堅持,您將會遇到更少的問題。 例如,本指南使用以下經驗法則:
1. 整數乘數比分數數更受歡迎。
2. 正常數比負常數更受歡迎。
3. 只要有可能,視圖應按布局順序顯示:從頭到尾,從上到下。
## 創建無歧義,可以滿足的布局
使用自動布局時,目標是提供一系列只有一個可能解決方案的方程。 模糊的約束有多種可能的解決方案。 不可滿足的約束條件沒有有效的解決方案。
一般來說,約束必須定義每個視圖的大小和位置。 假設已經設置了父視圖的大小(例如,iOS中場景的根視圖),則不含歧義,可滿足的布局需要每個視圖每個維度有兩個約束(不包括父視圖)。 但是,在選擇想要使用的約束時,有多種選擇。 例如,以下三種布局都會產生非歧義的,可滿足的布局(僅顯示水平約束):

* 第一個布局限制視圖的前沿相對于其父視圖前沿的前沿。 它也給視圖一個固定的寬度。 后緣的位置可以基于父視圖的尺寸和其他約束來計算。
* 第二個布局相對于其父視圖前沿的前沿約束了視圖的前沿。 它還限制視圖相對于父視圖的后沿的后沿。 視圖的寬度可以根據父視圖的大小和其他約束來計算。
* 第三種布局相對于其父視圖前沿的前沿約束視圖的前沿。 它也中心對齊視圖和父視圖。 寬度和后緣的位置都可以基于父視圖的尺寸和其他約束來計算。
注意每個布局有一個視圖和兩個水平約束。 在每種情況下,約束都完全定義視圖的寬度和水平位置。 這意味著所有的布局都會沿水平軸產生一個不含糊的,可以滿足的布局。 但是,這些布局并非同等有用。 考慮當超視圖寬度改變時會發生什么。
在第一個布局中,視圖的寬度不會改變。 大多數時候,這不是你想要的。 實際上,作為一般規則,您應該避免將常量大小分配給視圖。 自動布局旨在創建動態適應其環境的布局。 每當你給視圖一個固定的大小,降低了能力。
可能并不明顯,但第二個和第三個布局產生相同的行為:隨著超級視圖寬度的變化,它們在視圖和其超級視圖之間保持固定的邊距。 但是,它們并不一定是平等的。 一般來說,第二個例子更容易理解,但第三個例子可能更有用,尤其是當對齊多個項目時。 一如既往,為您的特定布局選擇最佳方法。
現在考慮一些更復雜的事情。 想象一下,你想在 iPhone 上并排顯示兩個視圖。 你要確保它們在所有方面都有很好的留白,并且它們總是具有相同的寬度。 它們也應該在設備旋轉時正確調整大小。
以下插圖以縱向和橫向顯示視圖:


那么這些約束是什么樣的? 下圖顯示了一個簡單的解決方案:

上述解決方案使用以下約束:
~~~
// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Blue.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Blue.bottom + 20.0
// Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0
~~~
遵循較早的經驗法則,該布局具有兩個視圖,四個水平約束和四個垂直約束。 雖然這不是一個可靠的指南,但它表明您正處于正確的軌道上。 更重要的是,約束唯一地指定了兩個視圖的大小和位置,產生了一個不含糊的可滿足的布局。 刪除任何這些約束,布局變得模糊。 添加其他約束條件,您可能會引入沖突。
不過,這不是唯一可能的解決方案。 這是一個同樣有效的方法:

將藍框的頂部與紅框的頂部對齊,而不是將藍框的頂部和底部固定到其父視圖。 同樣,將藍色框的底部與紅色框的底部對齊。 約束如下所示。
~~~
// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Red.top = 1.0 * Blue.top + 0.0
Red.bottom = 1.0 * Blue.bottom + 0.0
//Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0
~~~
該示例仍然有兩個視圖,四個水平約束和四個垂直約束。 它仍然產生一個不含糊的,可以滿足的布局。
> 但哪個更好?
> 這些解決方案都生成有效的布局。 那哪個更好?
> 不幸的是,客觀證明一種方法嚴格優于另一種方法實際上是不可能的。 每個人都有自己的長處和短處。
> 當視圖被刪除時,第一種解決方案更加強大。 從視圖層次結構中刪除視圖也會刪除引用該視圖的所有約束。 所以,如果你刪除了紅色視圖,藍色視圖留下了三個約束。 您只需添加一個約束,然后再次擁有有效的布局。 在第二種解決方案中,刪除紅色視圖會使藍色視圖只有一個約束。
> 另一方面,在第一種解決方案中,如果要使視圖的頂部和底部對齊,則必須確保它們的頂部和底部約束使用相同的常量值。 如果你改變一個常數,你必須記得改變另一個。
## 約束不等式
到目前為止,所有的樣本都顯示出平等的限制,但這只是故事的一部分。 約束也可以表示不平等。 具體而言,約束的關系可以等于,大于等于或小于等于。
例如,可以使用約束來定義視圖的最小、最大大小(清單3-3)。
###### 清單3-3指定最小和最大大小
~~~
// Setting the minimum width
View.width >= 0.0 * NotAnAttribute + 40.0
// Setting the maximum width
View.width <= 0.0 * NotAnAttribute + 280.0
~~~
一旦開始使用不等式,每個維度每個視圖的兩個約束就會崩潰。你總是可以用兩個不等式來代替一個等式。在清單 3-4 中,單相等關系和兩個不等式產生相同的行為。
###### 清單 3-4 用兩個不等式替換一個等式
~~~
// A single equal relationship
Blue.leading = 1.0 * Red.trailing + 8.0
// Can be replaced with two inequality relationships
Blue.leading >= 1.0 * Red.trailing + 8.0
Blue.leading <= 1.0 * Red.trailing + 8.0
~~~
反過來并不總是正確的,因為兩個不等式并不總是等價于一個等式。 例如,代碼清單 3-3 中的不等式限制了視圖寬度的可能值范圍 - 但是它們本身并沒有定義寬度。 仍然需要額外的水平約束來定義視圖在此范圍內的位置和大小。
## 約束優先級
默認情況下,所有約束都是必需的。 自動布局必須計算滿足所有約束的解決方案。 如果不能,則會出現錯誤。 自動布局向控制臺輸出有關不可滿足約束的信息,并選擇其中一個約束來中斷。 然后重新計算解決方案(除去被破壞的約束)。 有關更多信息,請參閱 [Unsatisfiable Layouts](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ConflictingLayouts.html#//apple_ref/doc/uid/TP40010853-CH19-SW1) 。
也可以創建可選約束。 所有約束的優先級都在1到1000之間。優先級為1000的約束條件是必需的。 所有其他限制都是可選的。
計算解決方案時,自動布局嘗試按優先順序從最高到最低滿足所有約束。 如果它不能滿足可選約束,則跳過該約束并繼續到下一個約束。
即使一個可選約束不能滿足,它仍然可以影響布局。如果在跳過約束后的布局中有任何歧義,系統將選擇最接近約束的解決方案。在這種情況下,不滿足的可選約束可以作為一種拉動他們視圖的力量。
可選的制約因素和不平等往往是相輔相成的。 例如,在清單 3-4 中,可以為這兩個不等式提供不同的優先級。 可能需要大于等于關系(優先級為1000),小于等于關系的優先級較低(優先級為250)。 這意味著藍色視圖離紅色點不能小于8.0點。 但是,其他約束可能會將它拉遠。 盡管如此,可選約束會將藍色視圖拉向紅色視圖,以確保它盡可能接近8.0點間距,同時考慮到布局中的其他約束條件。
> 注意
> 不要覺得有義務全部使用1000個優先級值。 實際上,優先級應該圍繞系統定義的低(250),中(500),高(750)和必需(1000)優先級。 可能需要制定高于或低于這些值一個或兩個點的約束,以幫助防止關系。 如果遠遠超出這個范圍,可能需要重新審視布局邏輯。
> 有關 iOS 上預定義約束常量的列表,請參閱 [UILayoutPriority](https://developer.apple.com/documentation/uikit/uilayoutpriority) 枚舉。 對于OS X,請參閱 Layout Priorities 常量。
## 內在內容大小
到目前為止,所有的例子都使用約束來定義視圖的位置和大小。 但是,根據當前內容,某些視圖具有自然大小。 這被稱為它們的內在內容大小。 例如,按鈕的內在內容大小是其標題的大小加上一個小的余量。
并非所有視圖都具有內在內容大小。 對于有的視圖,內在內容大小可以定義視圖的高度,寬度或兩者。 表 3-1 列出了一些示例。
###### 表 3-1 常用控件的內部內容大小。
| 視圖 | 內在內容大小 |
| --- | --- |
| UIView and NSView | 無 |
| Sliders | 僅定義寬度(iOS)。定義寬度,高度或兩者 - 取決于滑塊的類型(OS X)。 |
| Labels, buttons, switches, and text fields | 定義高度和寬度。 |
| Text views and image views | 內部內容的大小可以變化。 |
內在內容大小基于視圖的當前內容。 標簽或按鈕的內在內容大小基于顯示的文本量和使用的字體。 對于其他視圖,內在內容的大小更加復雜。 例如,空圖像視圖不具有內在內容大小。 但是,只要添加圖像,其內在內容大小就會設置為圖像的大小。
文本視圖的內在內容大小因內容,是否啟用滾動以及應用于視圖的其他約束而異。 例如,啟用滾動功能后,該視圖不具有固有內容大小。 在禁用滾動的情況下,默認情況下視圖的內在內容大小是基于文本的大小計算的,沒有任何換行。 例如,如果文本中沒有換行,它將計算將內容布置為單行文本所需的高度和寬度。 如果添加約束來指定視圖的寬度,則內在內容大小將定義顯示指定其寬度的文本所需的高度。
自動布局使用每個維度的一對約束來表示視圖的內在內容大小。 內容擁抱將內部視圖拉向內部,使其適合周圍的內容。 壓縮阻力將視圖向外推,以便它不剪裁內容。

這些約束是使用清單 3-5 中顯示的不等式定義的。 這里,IntrinsicHeight 和 IntrinsicWidth 常量表示視圖內在內容大小的高度和寬度值。
###### 清單3-5壓縮阻力和內容擁抱方程
~~~
// Compression Resistance
View.height >= 0.0 * NotAnAttribute + IntrinsicHeight
View.width >= 0.0 * NotAnAttribute + IntrinsicWidth
// Content Hugging
View.height <= 0.0 * NotAnAttribute + IntrinsicHeight
View.width <= 0.0 * NotAnAttribute + IntrinsicWidth
~~~
每個約束都可以有其自己的優先級。默認情況下,視圖對其內容擁有使用250優先級,而對于其壓縮抵抗則使用750優先級。因此,拉伸視圖比縮小視圖更容易。 對于大多數控制,這正是想要的行為。例如,可以安全地拉伸大于其內在內容大小的按鈕; 但是,如果你縮小它的內容,它的內容可能會被剪輯掉。請注意,Interface Builder 可能會偶爾修改這些優先級以幫助防止關聯。有關更多信息,請參閱 [Setting Content-Hugging and Compression-Resistance Priorities](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithConstraintsinInterfaceBuidler.html#//apple_ref/doc/uid/TP40010853-CH10-SW2)。
只要有可能,請在布局中使用視圖的內在內容大小。 它可以讓布局動態適應視圖內容的變化。 它還減少了創建無歧義,無沖突布局所需的約束數量,但需要管理視圖的內容擁抱和抗壓縮(CHCR)優先級。 以下是處理內在內容大小的一些準則:
* 在拉伸一系列視圖以填充空間時,如果所有視圖具有相同的內容擁抱優先級,則布局不明確。 自動布局不知道應該拉伸哪個視圖。一個常見的例子是標簽和文本字段。 通常情況下,希望文本字段進行拉伸以填充額外空間,同時標簽保持其固有內容大小。 為確保這一點,請確保文本字段的水平內容擁抱優先級低于標簽。實際上,這個例子非常常見,Interface Builder 自動為您處理它,將所有標簽的內容擁抱優先級設置為251。如果以編程方式創建布局,則需要自己修改內容擁抱優先級。
* 當具有不可見背景(如按鈕或標簽)的視圖意外伸展超出其內在內容大小時,通常會出現奇怪和意外的布局。 實際問題可能并不明顯,因為文本只出現在錯誤的位置。 為了防止不必要的拉伸,增加內容擁抱優先級。
* 基線約束僅適用于處于內在內容高度的視圖。 如果視圖被垂直拉伸或壓縮,則基線約束不再正確對齊。
* 一些視圖,如開關,應始終以其固有內容大小顯示。 根據需要增加他們的 CHCR 優先級以防止拉伸或壓縮。
* 避免給予視圖CHCR的優先級。通常認為錯誤的大小比不小心造成沖突要好。如果視圖應該始終是其固有的內容大小,那么可以考慮使用非常高的優先級(999)。這種方法通常使視圖不被拉伸或壓縮,但仍然提供了一個緊急壓力閥,以防視圖顯示在比預期的更大或更小的環境中。
## 內在內容大小與合適大小
內在內容大小作為自動布局的輸入。 當視圖具有內在內容大小時,系統會生成約束來表示該大小,并使用約束來計算布局。
另一方面,合適尺寸是自動布局引擎的輸出。 它是根據視圖的約束為視圖計算的大小。 如果視圖使用“自動布局”布局其子視圖,則系統可能會根據視圖的內容計算視圖的合適尺寸。
堆棧視圖就是一個很好的例子。 除了任何其他限制,系統都會根據其內容和屬性計算堆棧視圖的大小。 在許多方面,堆棧視圖的行為就好像它具有內在內容大小:可以使用僅一個垂直和一個水平約束來定義其位置來創建有效的布局。 但其大小是通過自動布局計算的 - 它不是自動布局的輸入。 設置堆棧視圖的 CHCR 優先級不起作用,因為堆棧視圖沒有固有內容大小。
如果需要相對于堆棧視圖外的項目調整堆棧視圖的合適大小,請創建顯式約束來捕獲這些關系,或者修改堆棧內容相對于堆棧外項目的 CHCR 優先級。
## 約束屬性值
自動布局中的值總是以點為單位。 但是,這些測量的確切含義可能會因涉及的屬性和視圖的布局方向而異。
| 圖標| 自動布局屬性 | 值 | 備注 |
| --- | --- | --- |--- |
|  | Heigh | 視圖的大小 |這些屬性可以賦予常量值或與其他高度和寬度屬性結合使用。 這些值不能為負數。 |
|  | Width | 視圖的大小 |這些屬性可以賦予常量值或與其他高度和寬度屬性結合使用。 這些值不能為負數。 |
|  | Top | 隨著向下移動屏幕,值會增加 |這些屬性只能與Center Y,Top,Bottom和Baseline屬性組合。 |
|  | Bottom | 隨著向下移動屏幕,值會增加 |這些屬性只能與Center Y,Top,Bottom和Baseline屬性組合。 |
|  | Baseline | 隨著向下移動屏幕,值會增加 |這些屬性只能與Center Y,Top,Bottom和Baseline屬性組合。 |
|  | Leading | 隨著朝著后緣移動,這些值會增加。 對于從左到右的布局方向,當您向右移動時,值會增加。 對于從右向左布局方向,向左移動時值會增加。 | 這些屬性只能與Leading,Trailing或Center X屬性組合。|
|  | Trailing | 隨著朝著后緣移動,這些值會增加。 對于從左到右的布局方向,當您向右移動時,值會增加。 對于從右向左布局方向,向左移動時值會增加。 | 這些屬性只能與Leading,Trailing或Center X屬性組合。|
|  | Left |隨著您向右移動,值會增加。 |這些屬性只能與 Left,Right 和 Center X 屬性組合。避免使用左右屬性。 改用 Leading 和 Trailing 。 這允許布局適應視圖的閱讀方向。默認情況下,閱讀方向是根據用戶設置的當前語言確定的。 但是,您可以在必要時重寫此操作。 在 iOS 中,在包含約束的視圖上設置 [semanticContentAttribute](https://developer.apple.com/documentation/uikit/uiview/1622461-semanticcontentattribute) 屬性(受約束影響的所有視圖的最近共同祖先),以指定在從左到右和從右到左語言之間切換時是否應翻轉內容的布局。
|  | Right | 隨著您向右移動,值會增加。 | 這些屬性只能與 Left,Right 和 Center X 屬性組合。避免使用左右屬性。 改用 Leading 和 Trailing 。 這允許布局適應視圖的閱讀方向。默認情況下,閱讀方向是根據用戶設置的當前語言確定的。 但是,您可以在必要時重寫此操作。 在 iOS 中,在包含約束的視圖上設置 [semanticContentAttribute](https://developer.apple.com/documentation/uikit/uiview/1622461-semanticcontentattribute) 屬性(受約束影響的所有視圖的最近共同祖先),以指定在從左到右和從右到左語言之間切換時是否應翻轉內容的布局。|
|  | Center X | 解釋基于方程中的其他屬性。 | Center X可以與Center X,Leading,Trailing,Right和Left屬性結合使用。Center Y可以與Center Y,Top,Bottom和Baseline屬性結合使用。|
|  | Center Y | 解釋基于方程中的其他屬性。 | Center X可以與Center X,Leading,Trailing,Right和Left屬性結合使用。Center Y可以與Center Y,Top,Bottom和Baseline屬性結合使用。|
>原文地址
>[Anatomy of a Constraint ](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW1)