UI自動布局

來源:互聯網
上載者:User

UI自動布局

當內部或外部條件發生變化的時候,自動重新計算視圖的位置和大小。

對於ios應用,可以觸發自動布局的變化可以分為外部變化和內部變化:
外部的變化可以是裝置的方向改變,ipad的分屏,不同尺寸的螢幕等。
內部的變化可以是內容的變化(載入不同的圖片等),應用支援動態類型(允許使用者修改視圖),國際化(添加對多國使用者的支援)等。

ios的自動布局與基於frame布局的關係,就像安卓的相對布局與絕對布局的關係。基於frame的布局,也就是通過子視圖相對於父視圖座標的絕對位置和絕對大小,這種布局的好處在於座標位置確定,但是在面對多種尺寸螢幕,動態內容等問題時顯得非常的麻煩。而自動布局不同,它描述的是視圖與視圖之間的位置關係,無論螢幕大小或者內容如何改變,視圖之間的相對位置不變,視圖排版就不會亂掉。

自動布局需要通過約束constraint來實現。下面,先說明使用約束來實現自動布局的原理,後說明如何建立、修改、查看約束。

一、使用約束Constraint的自動布局(自動布局的原理)

布局,實際上是關於視圖位置的一系列方程。每個方程就是一個約束,一個約束通常描述的就是兩個屬性之間的數值關係,比如長是寬的多少倍、view2的起始位置是view1的計數位置加10等。

圖中的方程就是一個典型的約束。要約束的對象就是RedView(item1)的leading屬性(Attribute1),約束的具體內容就是BlueView(item2)的trailing屬性(Attribute2)乘上1.0再加上8.0。這個1.0叫做乘積因子multiplier,8.0是個常量constant。這幾個量的意義要明確,因為之後在建立約束的時候離不開這幾個量。

1.自動布局的常用屬性(還有幾個常用的在第8點)

視圖的四個邊界:leading(左邊界)、trailing(右邊界)、top(上邊界)、bottom(下邊界)
視圖的大小:height(高)、width(寬)
視圖的中心:center(中心座標)

2.使用自動布局的原則

首先,分清自動布局的屬性可以分為位置相關屬性和大小相關屬性。這兩個屬性之間不應該有任何關聯,具體要遵守以下原則:
不要用位置屬性去約束大小屬性。
不要給位置屬性賦常量值。
位置屬性的約束中乘積因子只能是1.0。
不要用水平方向的位置屬性去約束垂直方向的位置屬性。
不要用leading或trailing屬性來約束left或right屬性。

3.自動布局的約束的確是方程,不是指派陳述式

對於以方程形式存在的約束,並不是把等號右邊的值賦給等號左邊的值,而是通過解方程,使得等號兩邊相等。
因此,當方程有多個解,也就是說可能有多種布局方案可以滿足這些約束,這時候,就要從中確定一種方案,並且一直運用下去。比如說:整數的乘積因子比小數乘積因子優先,正數乘積因子比負數優先,views的排列按照從上至下,從左至右排列等等。這些規則需要自己去定,並且在一個項目中一直遵守。

4.建立一個確定的布局

使用自動布局的目標是可以產生一套確定的布局,也就是說同時滿足所有約束的布局方案儘可能地少。就像兩點確定一條直線一樣,要建立一個確定的布局,也有它的規則:兩個屬性確定一個view的一個維度位置。
如果確定一個view在水平方向上的位置(三種方法,對應左、中、右)
方法一:同時約束左邊界相對於父視圖的距離,以及視圖的寬
方法二:同時約束左右邊界相對於父視圖的距離
方法三:同時約束左邊界相對於父視圖的距離,以及視圖的中心位置

對以上三種方法的分析:
方法一:缺點是視圖的寬不可以隨父視圖的大小變化而變化
方法二:左右邊界相對於父視圖的距離固定(兩個距離可以不相等)
方法三:在需要安置許多中心對齊的視圖的時候,這個方法最便利

5.不等式

在約束的方程中,除了可以等號=,也可以有大於符號>、小於符號<、大於等於>=和小於等於<=。
不等式約束了屬性的變動範圍。可以用等式約束和不等式約束一起來確定布局。

6.約束的優先順序

預設情況下,自動布局會去計算出一個可以滿足所有約束條件的布局方案,如果計算不出來,就會把不能滿足的約束條件在控制台中輸出,並且選擇其中一個,不去遵守,然後再重新計算一次。
可以通過確定約束的優先順序來建立可選的約束。優先順序範圍在1-1000。優先順序為1000的約束就是必須要滿足的,其他的都是可選的。自動布局會按優先順序順序來計算布局方案,如果滿足不了可選的約束,就會跳過。如果跳過了某條約束以後,布局變得不確定(即有多種方案),那麼布局會選擇一個最接近那條約束的值來確定布局。

7.預設大小

有些控制項會有預設的大小,比如常見的Sliders有預設的寬,label、button、switch、textField等有預設的寬和高。
通常這些控制項的預設大小是基於它的內容的。就像imageView設定了圖片以後,預設就是圖片大小。imageView和textView的預設大小,會受到內容,還有是否允許滾動,還有其他的約束條件的影響。
iOS官方建議儘可能地使用視圖的預設大小,因為它允許視圖自動調整大小以適應其內容的變化,也可以減少要指定的約束數目。
使用預設大小的視圖,通常要聯合兩個與大小相關的約束,一個是“防止被展開”,一個是“防止被壓縮”,文檔上把它叫做CHCR(Content-Hugging and Compression-Resistance)。使用這兩個約束的重點是,注意調整視圖與視圖之間CHCR的優先順序的相對大小。比如說,在同一水平高度上有兩個視圖,都是使用預設大小的,當需要將這兩個視圖(都比較小)展開以填滿父視圖的寬度的時候,就會去比較這兩個視圖在水平方向上的“防止被展開”約束的優先順序,誰的優先順序小,相應的視圖就會被展開,而另一個視圖保持預設大小。如果這兩個優先順序相等,那自動布局機制就不知道應該展開哪個視圖了。
通常,使用預設大小的視圖布局出現意料之外的結果,很多時候,可能是被展開了。為了避免這種情況,防止控制項被展開,可以把“防止被展開”約束(Content-Hugging)的優先順序定大一點。
關於基準(baseline)的約束,只會對維持著預設高度的視圖起作用。

8. 可以用的參照物

有些屬性是可以在寫約束的時候作為參照物(約束方程中的item2)來用的:

(1)視圖控制器的根視圖的上、下導覽列——top layout guide 和 bottom layout guide。從iOS 7.0開始,UIViewController類,有了這兩個屬性topLayoutGuide和bottomLayoutGuide。

我這裡這裡所說的上、下導覽列,不是狹義的navigationBar和tabBar,而是泛指當前根視圖view的頂部和底部被遮擋了的部分(通常是導覽列之類)。具體來說,就是,當statusBar和半透明的navigationBar都可見的時候,topLayoutGuide就是狀態列加導覽列的部分,navigationBar不可見的時候,topLayoutGuide就是狀態列部分,如果連狀態列也隱藏了,那topLayoutGuide就沒有了。而對於bottomLayoutGuide來說,當tabBarController.tabBar或toolBar可見的時候,bottomLayoutGuide就是tabBar或toolBar部分,而當tabBar不可見的時候,bottomLayoutGuide就沒有了。
這兩個屬性本身也遵循了一個協議,UILayoutSupport協議。這個協議定義了4個屬性:length、bottomAnchor、heightAnchor、topAnchor。
length是什嗎?對於topLayoutGuide來說,就是topLayoutGuide的下邊界;對於bottomLayoutGuide來說,就是bottomLayoutGuide的上邊界 。
如果在代碼中直接存取viewController.topLayoutGuide會返回viewController.topLayoutGuide.length,訪問bottomLayoutGuide也是返回bottomLayoutGuide.length。因此在代碼中,也可以直接把topLayoutGuide和bottomLayoutGuide理解成是viewController.view的可視地區的上下邊界。
不過在可視化的autoLayout當中,還是把這兩者理解成viewController.view上下被遮擋的部分。
bottomAnchor、heightAnchor、topAnchor是協助在代碼實現中實現autoLayout用的,這裡先不細說。

(2)內邊距和內邊界——margins和margins guides

內邊距就是view的邊界到子視圖的最小距離,這個不多說。
UIViewController有兩個與內邊距有關的屬性,一個叫layoutMargins,一個叫layoutMarginsGuide。layoutMargins就是內邊距的大小。layoutMarginsGuide指的就是內邊界。
在寫約束的時候,通常都是相對於內邊界。預設的內邊距大小是8個點。

(3)文本邊界——readable Content Guides

UIViewController有一個與文本的邊界有關的屬性readableContentGuide。指的就是文本可以顯示的最大的地區的邊界。通常情況下,這個邊界和內邊界不會有太大的區別,只有在pad橫屏的時候會有比較明顯的區別。必要時,可以選擇讓布局相對於這個文本邊界,而不相對於內邊界。

二、約束的管理(自動布局的實操部分,約束的增刪改查)

約束的管理可以通過可視化的Interface Builder來實現,也可以通過代碼來是實現。第二節,也就是本節,先講InterfaceBuilder管理約束的方法。下一節講代碼的實現。
實現方法是通過直接拖動view,並且在xcode右側的interface Builder中設定相關約束來實現的。其實約束是可以用代碼來實現的。本節中先不說。下一節說。
對於之前都只是用代碼來建立view的同學有必要Crowdsourced Security Testing道怎麼建立xib和關聯xib。如果已經會的這一小段略過。
(1)方法一:建立ViewController的同時建立xib。和平時建立ViewController一樣,就是多勾選了Also create XIB file。這樣就會自動建立一個xib,並且和所建立的ViewController已經關聯了。

(2)方法二:自己建立xib,並與現有的ViewController關聯起來。
通過new File 建立xib。要把xib與現有的ViewController關聯起來,只要一步。
開啟xib,在左側找到這樣的表徵圖,點擊它,然後到右側找到中的選項卡,點擊第三個,然後就看到下面的Custom Class,在Class欄中選擇你要關聯的ViewController。

注意:需要設定xib的根視圖,方法是按住Ctrl同時,點擊上面file’s Owner,拖向view。即可。
注意:之後在建立ViewController時還需要注意,不要直接[[ViewController alloc ]init],而要使用initWithNibName:bundle:方法進行初始化,指定該xib檔案作為自己的根視圖。

回到正題,使用約束來實現自動布局:

1. 建立約束的兩個方法(1)方法一:

按住control+滑鼠點擊item1並拖向item2,就可以建立item1的某屬性對item2的某屬性的約束()。

(2)方法二:快速產生約束的四個工具(xcode底部,這種方法是 最便利最常用的):


第一個工具是可以快速建立stackView。
第二個工具可以快速建立有關視圖對齊的約束。
第三個工具可以快速建立有關視圖的寬、高、長寬比例、視圖與相鄰視圖間距的約束。
第四個工具可以實現約束的自動產生、約束的清除、視圖布局的更新等。
使用這四個工具的注意事項:
使用這幾個工具需要注意,要先點擊需要約束的視圖,然後再點擊某個工具。
對於第一個工具,需要先全選需要加入到stackView的視圖,然後點擊第一個工具。
對多個視圖進行多選,可以按住shift再逐個點選要選中的視圖。也可以按住Alt鍵,一次性框選需要選中的視圖。
對於第二個工具需要注意,由於該工具是關於對齊的,如果是要設定某個視圖相對於根視圖的對齊關係,只要選擇這個視圖,然後點擊第二個工具。如果要設定多個視圖之間的對其關係,需要先將這些視圖同時選中,然後再點擊第二個工具,進而進行設定。

就是設定某個視圖相對於父視圖的對其關係。勾選的是有效,未勾線的是無效的。horizontally in container是視圖中心的水平位置偏離父視圖中心水平位置的量。往左是負值,往右是正值。Vertically in Container是垂直方向。
第三個工具中管理檢視與相鄰視圖間距的地方,有一個constrain to margins,如果勾選,表明這些數值相對於內邊界,如果不勾選,就相對於根視圖的邊界(邊緣)。

還有,上面途中左邊和上面的數值是生效的,右邊和下面的數值不生效。也就是說線的虛實決定是否生效。
第四個工具分為兩欄,一欄是selected Views,一欄是All Views in View Controller,前者表明操作的對象為選中的視圖,後者表明操作的對象是這個視圖控制器中的所有視圖。Update Frame就是根據現有的約束更新視圖的frame。Update Constraints就是根據當前視圖布局來更新約束。

2. 查看約束的三個方法:

(1)方法一:xcode左側

(2)方法二:查看某個視圖的約束,先點擊該視圖,再點擊xcode右側的第五個表徵圖,就可以看到關於該視圖的所有約束()。

(3)方法三:通過小標籤或者線條:
選中要查看的視圖,然後可以看到他四周出現了小標籤和線條(代資料表條件約束),點擊它們就能在右側查看對應的約束。

標籤和線的顏色是有意義的:
通常綠色表示對這個視圖的約束可以唯一確定這個視圖的布局。
紅色表示仍未能確定唯一確定這個視圖的布局,或者這個約束有衝突。
橘色表示該約束確定的是視圖相對於根視圖的位置。

3. 編輯約束

(1)按查看約束的第一種方法或第三種方法查看約束,點擊需要編輯的約束。再到xcode右側,點擊()第四或第五個表徵圖,就可以看到約束的編輯地區。

(2)按查看約束的第二種方法查看約束,點擊約束旁邊的edit按鈕就可以編輯約束。

4.蘋果官方給出了一些有關自動布局的建議

不論用interfaceBuilder還是用代碼來實現自動布局,這些建議都是適用的。
(1)不用view的frame、bounds、center來指定view的形狀
(2)儘可能地使用stackView來布局
(3)約束盡量建立在view和其相鄰view之間
(4)避免給view指定固定的長和寬
(5)自動更新view的frame時要留心,尤其是對於約束條件不足的view。
(6)view的命名要有意義,方便布局時認得它們。
(7)使用leading和trailing約束,不要用left和right。
(8)在寫相對於view邊界的約束的時候,分兩種情況:
水平方向上的約束:對於大多數的控制項,約束應該相對於根視圖的內邊界
對於像小說閱讀器這樣文字布滿螢幕的情況,約束應該相對於文本邊界。
對於需要鋪滿根視圖寬度的視圖,約束可以相對於根視圖的邊界。
垂直方向上的約束:如果根視圖有被導覽列、tabBar等部分遮擋了,那麼約束應該相對於top margin和bottom margin。
(9)在使用autolayout來布局那些用代碼建立的view的時候,要把他們的translatesAutoresizingMaskIntoConstraints屬性設定為NO。這個屬性如果設為YES,系統會自動為這些view產生一些約束,這些約束可能會和我們設定的約束產生衝突。

三、用代碼建立約束

用代碼建立約束有三種方法:使用NSLayoutConstraint類,使用布局anchor(錨),使用Visual Format Language可視化格式語言。

1. 方法一(老方法):使用NSLayoutConstraint類

在約束的原理中有說道,約束方程的形式是這樣的,item1.attribute1 = multiplier × item2.attribute2 + constant。它的組成有7部分:item1、attribute1、item2、attribute2、relationship、multiplier、constant。NSLayoutConstraint類的功能在於,利用約束方程7個量來構造約束方程,並讓他成立。直接上例子最直觀:

NSLayoutConstraint(item: myView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .LeadingMargin, multiplier: 1.0, constant: 0.0).active = true

如上就建立了一個約束:myView.leading=view.leadingMargin×1.0+0.0。
這個方法來建立約束是個老方法,蘋果官方推薦,iOS 9.0之後出現的新方法,使用anchor來建立約束,看下一點。

2. 方法二:使用anchor

使用anchor的原理本質上和使用NSLayoutConstraint類是一樣的,但是表達更加簡潔了。
具體看NSLayoutAnchor類。

3. 使用Visual Format Language

直接看官網文檔吧Visual Format Language,那麼直觀。

總結

初學自動布局,目前覺得,實現autoLayout還是使用interface Builder提供的四個工具最方便。因為這四個工具可以一次性建立多條約束,用代碼的話得一條條地寫。

自動布局還有一種方法是用stackView,用它還可以實現動態增減視圖。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.