4. Working with Constraints in Interface Builder(在界面編輯器中使用約束)@Auto Layout Guide(自動布局指南)

翻譯@Auto Layout Guide(自動布局指南)


Getting Started(新手上路)

Working with Constraints in Interface Builder(在界面編輯器中使用約束)

(譯者:界面編輯器,Interface Builder,以下簡稱IB??)

通過IB創建約束,有三種方式:按住control拖拽;使用固定和對齊工具(譯者:Pin and Align Tools,其實是IB右下角的兩個按鈕,后者現在已經不叫Align,改為Add New Constraints);讓IB自動設置,然后手動修改約束。以上方式各有所長,大多數人會選擇一種熟練使用。但全部了解可以讓我們更好的根據需要選擇合適的方式。

不論使用哪種方式,都需要先將視圖從對象庫(Object Library)拖拽至畫布,并調整其大小和位置。IB會自動創建一組原型約束(prototyping constraints),相對于畫布左上角定義視圖的尺寸和位置。

至此,app已經可以編譯運行,展示界面。但隨著開發的進行,要逐步添加顯性約束。絕對不能發布使用原型約束的app。

一旦為視圖添加自定義約束,所有原型約束會被自動移除。此時,由于缺少約束,布局歧義。受歧義影響的約束顯示為紅色,Xcode報警告。

莫慌,繼續添加約束,直至布局完整。添加自定義約束時,必須做到有始有終。

更多關于修復警告和錯誤的信息,詳見章節Debugging Auto Layout(調試自動布局)

Control-Dragging Constraints(拖拽生成約束)

要在兩個視圖之間創建約束,可以按住control,將一個視圖拖動至另一個。

圖12

松開鼠標,從彈出的灰色菜單中選擇一個要添加的約束。

圖13

IB會根據約束的元素和拖拽的方向,生成可供選擇的約束。如果沿水平方向拖拽,則可以設置水平間距,垂直對齊。如果沿垂直方向拖拽,則可以設置垂直間距,水平對齊。當然,還有許多其他可用約束(例如相對尺寸)。

注意

可拖拽的對象不僅有畫布上的視圖,也可以是場景對象列表(scene's document outline)中的圖標。特別是畫布上找不到的元素,如布局參照(layout guide)。拖拽的對象是后者時,可供選擇的約束不再受拖拽方向影響。

IB根據視圖當前frame創建約束。因此,添加約束前,要將其移動到合適的位置。一般來說,根據系統建議擺放視圖(即視圖在畫布上移動時出現的藍色虛線),能夠得到符合預期的約束。況且,隨時可以修改約束。

拖拽讓我們迅速創建約束;然而,由于是根據當前frame創建,所以很容易產生偏差。要想更精確,可以在約束創建后逐一檢查并修改,或使用固定和對齊工具創建約束。

更多關于拖拽創建約束的信息,詳見自動布局幫助(Auto Layout Help)中同拖拽創建約束有關的內容。

Using the Stack, Align, Pin and Resolve Tools(堆疊,對齊,固定和問題解決工具的使用)

IB的右下角有四個布局工具:分別是堆疊,對齊,固定和問題解決。

圖14

想要更準確的創建約束,或一次創建多個約束,可以使用固定和對齊工具。無需提前排布視圖;只需大致確定視圖的位置,添加約束,勾選更新frame即可。系統會自動計算視圖位置和尺寸,并在畫布上更新。

Stack Tool(堆疊工具)

此工具可以快速創建堆疊視圖(UIStackView)。選中畫布上一個或多個視圖,單擊堆疊工具,這些視圖會被嵌入一個新的堆疊視圖中,其尺寸由內容決定。

注意

通過這種方式創建的堆疊視圖方向和對齊方式取決于內容視圖的相對位置。在屬性面板可以修改各項屬性的值。

Align Tool(對齊工具)

此工具可以快速對齊視圖。選中要對齊的視圖,點擊對齊工具,有菜單彈出,包含所有可能的對齊方式。

圖15

選中一個或多個對齊方式,點擊添加約束(Add Constraints)按鈕。相應的對齊約束會被添加至視圖。默認,這些約束沒有任何偏移量(即直接對其),視圖在畫布上的尺寸和位置也不會自動更新。但是,添加約束之前,可以進行設置。

使用對齊工具時,一般選擇兩個或以上視圖。然而,俯視圖內水平對齊(Horizontally in Container)或父視圖內垂直對齊(Vertically in Container)可以針對單個視圖添加。可以同時選中多個對齊方式,一次添加多條約束,但很少需要這樣做。

更多信息,詳見自動布局幫助中使用固定和對齊工具添加約束的相關內容。

Pin Tool(固定工具)

此工具能夠快速設置視圖的相對位置和尺寸。選中要固定的視圖,點擊固定工具,有菜單彈出,包含若干選項。

圖16

菜單的上半部分用于——相對于四周的鄰近元素——定義視圖四邊。對應的數字分別代當前間距;可以直接修改數字,也可以點擊隨后的三角形,選擇系統推薦值,或更改參照元素。下方的"保留邊距(Constrain to margins)"表示當參照元素是父視圖時,相對邊距還是相對四邊約束。(譯者:例如,相對于父視圖的leading約束,如不勾選,則意味著superView.Leading;反之,則意味著superView.Leading.Margin。)

圖17

菜單的下半部分用于定義視圖寬高。默認是當前frame值,可以修改。寬高比(Aspect Ratio)選項默認是當前frame寬高比,可以在添加約束后修改。

通常,我們一次只固定一個視圖 。然而,選中兩個或以上視圖,可以約束它們等高或等寬。可以一次創建多條約束,可以自動更新視圖frame。一旦確定要添加的約束,點擊添加約束按鈕(Add Constraints)即可。

更多信息,詳見自動布局幫助中使用固定和對齊工具添加約束的相關內容。

Resolve Auto Layout Issues Tool(問題解決工具)

此工具可以解決許多常見布局問題。菜單的上部選項只影響選中視圖,下部選項影響場景中所有元素。

圖18

借助此工具,我們可以根據視圖約束更新其frame;也可以根據視圖frame更新其約束。還可以添加缺失約束,清除約束,或者重置視圖,替換為系統推薦約束。

添加或重置約束的操作在章節:Letting Interface Builder Create Constraints(讓界面編輯器替我們創建約束)中有更詳細的討論。

Letting Interface Builder Create Constraints(界面編輯器自動創建約束)

IB可以自動創建部分,甚至所有約束:其基于視圖當前frame推測出合適的約束。所以請注意視圖的擺放——即使是微小的偏差,也可能導致截然不同的布局結果。

想要利用這一特性,點擊問題解決工具,選擇重置為推薦約束(Reset to Suggested Constraints)。IB會自動為選中視圖(或所有視圖)添加約束,產生明確,可滿足的布局。

也可以自行添加一部分約束,然后點擊問題解決工具,選擇添加缺失約束(Add Missing Constraints)。系統會補全約束,類似的,適用于選中視圖或所有視圖。

這一特性讓我們快速創建明確,可滿足的布局。然而,除非界面足夠簡單,否則布局結果可能會和預想不同。所以,要多測試,確保結果符合預期。

Finding and Editing Constraints(查找和編輯約束)

添加約束后,還需要能夠定位,查看,修改約束。訪問約束的途徑有很多,代表組織和呈現約束的不同方式。

Viewing Constraints in the Canvas(查看畫布上的約束)

同選中視圖相關的約束會被顯示在畫布上,用藍色線條表示。線條形狀,兩端樣式以及顏色能夠透露許多關于約束的信息。

  • 工型直線(兩端為T型):代表空間長度。可以是視圖寬高,也可以是兩個視圖的間距。
  • 一般直線(兩端無樣式):代表四邊對齊。例如,對齊兩個視圖的前邊。另外,視圖間距為0pt時,也用這種線條表示。
  • 實線:代表"必要(Required)"約束(優先級等于1000)。
  • 虛線:代表優先級小于1000的約束。
  • 紅線:代表受此約束影響的視圖的布局有歧義,或無法滿足。更多信息,可以通過問題導航面板或場景對象列表上方的箭頭查看。
  • 橙線:代表受此約束影響的視圖的frame不正確。橙色虛線代表正確的frame。點擊問題解決工具,選擇更新frame(Update Frames)即可解決。
  • 藍線:代表受此約束影響的視圖的布局明確,可滿足,其frame正確。
  • 等號標志:代表等高或等寬的約束。受影響的視圖會相應的位置顯示一條藍色T型直線,中間有"="標志。
  • 大于等于或小于等于標志:代表規定了大于等于或小于等于關系的約束,中間有">="或"<="標志。
圖19
Listing Constraints in the Document Outline(通過場景對象列表查看約束)

所有約束都可以在場景對象列表中找到,歸類在所屬視圖下方:參與約束的兩個元素的公共父視圖,持有約束。據此,單個視圖持有自身及其子視圖,上下布局參照被根視圖持有。

圖20

雖然約束散落在列表中,但大多數都被根視圖持有。展開完整列表可以查看所有約束。

列表通過偽代碼表示約束,內容較長,(約束之間)區分度不高,特別一個元素下多個約束并排顯示時。可以調整列表寬度,顯示完整信息。選中列表中的約束,畫布上相應約束會高亮。

如果布局簡單,通過場景對象列表查看約束的確非常方便。但隨著布局復雜度增加,定位約束會變的非常困難。因此,最好以視圖為單位查看約束:在畫布上選中視圖,查看相關約束;或通過尺寸面板查看相關約束。

Finding Constraints in the Size Inspector(通過尺寸面板中查看約束)

尺寸面板會顯示所有同當前視圖相關的約束。必要約束(優先級1000)表示為實線,可選約束表示為虛線。約束的相關信息在下方顯示,羅列了參與的元素和屬性;還可能包含關系,常量,系數及比例的信息。

圖21

上圖中上方的圖例展示了視圖的哪些屬性受約束影響。選中某個屬性,下方列表會被過濾,只留下影響當前屬性的約束。

更多信息,詳見自動布局幫助中同查看元素約束有關的內容。

Examining and Editing Constraints(檢查和編輯約束)

約束一旦被選中,屬性面板會展示相關信息。例如關系等式的所有組成部分:元素,關系,常量,系數;另外還包括優先級和識別符等信息。

圖22

注意

identifier屬性允許我們為約束命名,從而在查看控制臺信息和debug時更好的識別約束。

另外,可以將其標記為占位約束(Placeholder)。占位約束只在設計xib時有效。運行時的界面不包含這些約束,因此無效。舉例來說,布局中有需要動態添加的約束,我們可以使用占位約束代表動態約束。這樣,可以消除xib的布局警告,查看運行時的布局效果。

常量,優先級,系數,關系,識別符,是否占位,都可以直接修改;但參與約束的元素無法。元素的位置可以對調(當然系數和常量需要手動調整)。即可以修改元素屬性,但不能更改元素。如果確實需要更改,請刪除約束,重新添加。

也可以在視圖的尺寸面板中修改約束。點擊任意約束后的Edit(編輯)按鈕,在彈出的菜單中修改關系,常量,優先級和系數。要做更多修改,雙擊約束,切換至約束屬性面板。

圖23

更多信息,詳見自動布局幫助中同編輯約束相關的內容。

Setting Content-Hugging and Compression-Resistance Priorities(內縮和外擴優先級)

要設置視圖的內縮和外擴優先級(CHCR priorities),首先選中視圖,然后打開尺寸面板,找到內縮和外擴優先級設置。

圖24

還可以設置視圖的占位(Placeholder)固有尺寸。默認,系統使用intrinsicContentSize的返回值,作為視圖固有尺寸。然而,如果固有尺寸能夠在設計xib時確定,則可以設置一個占位值。這個尺寸只在設計xib時有效,運行時無效。

更多信息,詳見自動布局幫助中同設置視圖占位固有尺寸相關的內容。

iOS-Only Features(iOS獨有特性)

iOS有一些針對自動布局的獨有特性,包括布局參照(layout guide),視圖留白(layout margin),可讀內容參照(readable content guide),以及語義內容(semantic content)。

Top and Bottom Layout Guides(上下布局參照)

布局參照代表當前控制器可視范圍的上下邊界。例如,要避免視圖被透明或半透明的欄位阻擋(UIKit bar,如status bar,navigtaion bar和tab bar),請相對布局參照約束。

布局參照遵守協議UILayoutSupport,其定義屬性length,用于表示布局參照到視圖上下邊緣的距離。特別注意:

  • 對于上方布局參照(top layout guide),其代表控制器視圖上邊與所有上方欄位的最下邊的距離,單位pt。(例如,上方有狀態欄,然后是導航欄,則此時length = 狀態欄高度 + 導航欄高度)
  • 對于下方布局參照(bottom layout guide),其代表控制器視圖下邊與下方欄位的最上邊的距離,單位pt。(例如,下方有tab bar,則此時length = tabBar.height)

布局參照可以參與約束,定義視圖的上下及高度。通常,相對于上方布局參照的下邊,或下方布局參照的上邊約束視圖。另外,代碼創建約束時,還可以使用屬性topAnchorbottomAnchorheightAnchor

相對根視圖(root view)的上下邊創建約束時,布局參照會自動作為備選項出現。如果它們是視圖的最鄰近元素,則默認選中。使用固定工具(Pin tool)時,通過點擊相應位置的三角形,可以在視圖邊緣和布局參照之間切換。

Layout Margins(布局留白)

自動布局中,視圖有留白(margin)。所謂留白,就是視圖子視圖與其邊緣之間的推薦距離。通過訪問視圖屬性layoutMarginslayoutMarginsGuide獲取相關信息。屬性layoutMargins通過結構體UIEdgeInsets定義留白。只讀屬性layoutMarginsGuide通過對象UILayoutGuide給出留白。此外,可以通過視圖屬性preservesSuperviewLayoutMargins規定視圖留白和父視圖留白之間的關系。

視圖四周留白默認為8pt。當然,可以根據需要修改。

注意

系統自動設置和管理控制器根視圖的留白。其上下留白為0pt,便于將內容置于欄位(UIKit bar)下方(如有)。兩側留白根據控制器的呈現方式變化,一般為16或20pt。以上數值無法修改。

相對于父視圖約束時,一般參照其留白,而非邊緣。UIKit中的枚舉NSLayoutAttribute包含一組枚舉值,分別代表各個方向留白:上下,左右,前后;以及基于留白的水平和垂直中心。

在IB中,通過control拖拽生成的,相對于父視圖的約束默認參照留白。使用固定工具創建約束時,勾選"Constrain to marings",可以在留白和邊緣之間切換。類似的,在屬性面板中編輯約束時,在元素的下拉菜單中勾選"Relative to margin(相對留白)";其作用和"Constrain to margins"一致。

最后,代碼創建約束時,使用父視圖屬性layoutMarginsGuide。其包含一組錨點,相對這些錨點創建約束,代碼可讀性更好。

Readable Content Guides(可讀內容參照)

視圖屬性readableContentGuide定義視圖中文本對象的最適宜寬度。理想情況下,用戶無需轉動頭部,即可閱讀全部內容。

這個區域相對四邊留白居中,且絕不會超出四邊留白。其尺寸會根據系統動態字體的大小調整。如果字體變大,則尺寸變寬;因為用戶在閱讀時,與設備的距離更遠。

可以在IB中設置視圖留白等同于布局留白,還是可讀內容參照。選中視圖(此時一般是控制器根視圖),打開尺寸面板,勾選"Follow Readable Width"。如此一來,任何相對于留白的約束,就是相對于可讀內容參照。

注意

對于大多數設備來說,無論誰扮演視圖留白(布局留白或可讀內容參照),區別都很小,甚至沒有。只有在橫屏下的iPad,才能看出明顯的區別。

Semantic Content(語義內容)

使用前后(leading和trailing)布局時,視圖內容方向會自動根據語言的閱讀方向調整(例如英語的閱讀方向是從左至右,而阿拉伯語從右至左)。然而,某些視圖無須變化,如模擬方位的按鈕(上,下,左,右)。

視圖屬性semanticContentAttribute規定內容是否需要根據語言的閱讀調整。

點擊視圖屬性面板中的"Semantic",彈出菜單,選項如下:"Unspecified"表示總是反轉;"Spatial(空間內容)","Playback(播放器)",和"Force Left-to-Right(強制從左至右)",表示前邊等同于左邊,后邊等同于右邊;"Force Right-to-Left",表示前邊等同于右邊,后邊等同于左邊。

Rules of Thumb(規則總結)

下述規則能夠幫助我們順利使用自動布局。當然,一切都有例外;但在違背規則之前,三思而后行。

  • 不手動設置視圖的frame,bounds及center。

  • 盡量使用堆疊視圖

    堆疊視圖能夠自動管理其布局,免除了手動添加約束的麻煩。除非無法滿足需求,否則不手動布局。

  • 參照最鄰近元素設置約束。

    假設有兩個相鄰按鈕A和B,約束B的前邊時,參照A的后邊,而非父視圖的前邊(即跨過了A)。

  • 避免固定寬高。

    自動布局的優勢在于動態響應變化,固定寬高意味著將放棄這種優勢。然而,可以限定款高的最大值或最小值。

  • 設置約束無從下手時,可嘗試使用固定和對齊工具。雖然效率不如control拖拽,但可以明確構成約束的元素和數值,從而有助于我們理清思路。

  • 小心使用自動更新frame。如果視圖布局有歧義或沖突,則結果無法預期。視圖有可能從畫布上消失,其原因可以是寬或高為0,也可以是視圖位于屏幕之外。

    當然,frame更新后可以撤銷。

  • 為約束添命名,便于區分。

    按鈕或標簽的名稱就是其文本內容。對于其他視圖來說,需要在身份面板中設置"label",或者在場景對象列表中雙擊視圖,填入名稱。

  • 創建約束時,盡量使用前后(leading和trailing),而非左右(left和right)。

    通過視圖屬性semanticContentAttribute(iOS)或userInterfaceLayoutDirection(OS X)定義前后。

  • iOS中,參照控制器根視圖四邊添加約束時,最好使用如下約束:

    • 水平約束。對于大多數控件來說,緊貼留白布局即可(即偏移量為0pt)。系統會根據設備類型和控制器的呈現方式自動調整邊距。

      對于占據整個橫向空間的文本視圖來說,相對可讀內容參照布局。

      對于需要完全填充整個空間的視圖來說(例如背景圖片),相對前后布局。

    • 垂直約束。如果視圖需要延伸至欄位(UIKit bar)下方,相對上下留白布局。這是滾動視圖(scroll view)的常見用法,因為其內容需要在欄位下方滾動。但注意,可能需要調整滾動視圖屬性contentInsetscrollIndicatorInsets,以確保內容的初始位置正確。

      如果視圖不需出現在欄位下方,則相對上下布局參照約束。

  • 代碼布局時,必須將視圖屬性translatesAutoresizingMaskIntoConstraints置為NO。系統默認根據視圖frame及其自動縮放掩碼生成一組約束,添加至視圖;這些約束很可能同自定義約束產生沖突。

  • 注意OS X和iOS計算布局的方式不同。
    在OS X中,自動布局影響窗口的內容和尺寸。

    在iOS中,場景的尺寸和布局不可更改,自動布局僅影響場景的內容。

    上述區別看似無關緊要,但對于布局設計,特別是約束的優先級順序,有著深遠的影響。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容