iOS自動佈局學習(UIView+AutoLayout)

  自動佈局雖然在iOS6的時候已經推出,不過因爲各個緣由並無被開發組普遍使用。一方面是你們的app支持版本都是低於iOS6的,另外一方面來講是Xcode支持木有如今這麼好。之前因爲iPhone設備相對固定,因此在純代碼,純座標的佈局下很流行,不過如今隨着iPhone6發佈,若是還要寫一大堆亂七八糟的絕對座標去適配,那工做量和維護成本是很大的。git

下面的一些基礎直接拿小夥伴分享整理的吧,以後向你們推薦一個開源的庫,對AutoLayout進行了封裝,用起來很簡單也高效。github

1.AutoLayout是什麼?

使用一句Apple的官方定義的話
 
  AutoLayout是一種基於約束的,描述性的佈局系統。
  Auto Layout Is a Constraint-Based, Descriptive Layout System.
 
關鍵詞:
  • 基於約束 - 和以往定義frame的位置和尺寸不一樣,AutoLayout的位置肯定是以所謂相對位置的約束來定義的,好比x座標爲superView的中心,y座標爲屏幕底部上方10像素
  • 描述性 - 約束的定義和各個view的關係使用接近天然語言或者可視化語言(VFL)的方法來進行描述
  • 佈局系統 - 即字面意思,用來負責界面的各個元素的位置。
總而言之,AutoLayout爲開發者提供了一種不一樣於傳統對於UI元素位置指定的佈局方法。之前,不管是在IB裏拖放,仍是在代碼中寫,每一個UIView都會有本身的frame屬性,來定義其在當前視圖中的位置和尺寸。使用AutoLayout的話,就變爲了使用約束條件來定義view的位置和尺寸。這樣的 最大好處是一舉解決了不一樣分辨率和屏幕尺寸下view的適配問題,另外也簡化了旋轉時view的位置的定義,原來在底部之上10像素居中的view,不論在旋轉屏幕或是更換設備(iPad或者iPhone5或者之後可能出現的mini iPad)的時候,始終還在底部之上10像素居中的位置,不會發生變化。總結 
使用約束條件來描述佈局,view的frame會依據這些約束來進行計算Describe the layout with constraints, and frames are calculated automatically.

1.1AutoLayout和Autoresizing Mask的區別

在iOS6以前,關於屏幕旋轉的適配和iPhone,iPad屏幕的自動適配,基本都是由Autoresizing Mask來完成的。可是隨着你們對iOS app的要求愈來愈高,以及已經以及從此可能出現的多種屏幕和分辨率的設備來講,Autoresizing Mask顯得有些落伍和遲鈍了。AutoLayout能夠完成全部原來Autoresizing Mask能完成的工做,同時還可以勝任一些原來沒法完成的任務,其中包括:
  • AutoLayout能夠指定任意兩個view的相對位置,而不須要像Autoresizing Mask那樣須要兩個view在直系的view hierarchy中。
  • AutoLayout沒必要須指定相等關係的約束,它能夠指定非相等約束(大於或者小於等);而Autoresizing Mask所能作的佈局只能是相等條件的。
  • AutoLayout能夠指定約束的優先級,計算frame時將優先按照知足優先級高的條件進行計算。
總結
Autoresizing Mask是AutoLayout的子集,任何能夠用Autoresizing Mask完成的工做均可以用AutoLayout完成。AutoLayout還具有一些Autoresizing Mask不具有的優良特性,以幫助咱們更方便地構建界面
 
 
2.基本原理
假設有一個按鈕,你想把它放置在屏幕的中央。視圖中心和按鈕中心的相對位置能夠簡
單地定義成以下:

·按鈕的 center.x 至關於視圖中心的 center.x數組

 
·按鈕的 center.y 至關於視圖中心的 center.y

蘋果發現不少的 UI 組件的位置可使用一個簡單的方程等式獲得解決:
 
item1.attribute = multiplier ⨉ item2.attribute + constant

例如:使用這個方程式,咱們能夠很容易地將一個按鈕放置到他的父視圖中,以下所 示:app

    Button.center.x=(button.superview.center.x*1)+0
    Button.center.y=(button.superview.center.y*1)+0
 
    
即:y = m*x + b
3.相關知識
3.1 使用線性公式添加描述
iOS6 引入類來描述約束條件:
NSLayoutConstraint NS_CLASS_AVAILABLE_IOS(6_0) 
方法:constraintWithItem
[NSLayoutConstraint constraintWithItem:(id)
                                 attribute:(NSLayoutAttribute)
                                 relatedBy:(NSLayoutRelation)
                                    toItem:(id)
                                 attribute:(NSLayoutAttribute)
                                multiplier:(CGFloat)
                                  constant:(CGFloat)];

 


(item1.attribute = multiplier ⨉ item2.attribute + constant)
NSLayoutAttribute:
typedef  NS_ENUM(NSInteger, NSLayoutAttribute) {
     NSLayoutAttributeLeft = 1,                      //左側
     NSLayoutAttributeRight,                         //右側
     NSLayoutAttributeTop,                           //上方
     NSLayoutAttributeBottom,                        //下方
     NSLayoutAttributeLeading,                       //首部
     NSLayoutAttributeTrailing,                      //尾部
     NSLayoutAttributeWidth,                         //寬度
     NSLayoutAttributeHeight,                        //高度
     NSLayoutAttributeCenterX,                       //X軸中心
     NSLayoutAttributeCenterY,                       //Y軸中心
     NSLayoutAttributeBaseline,                      //文本底標線
                                                                                                                                                    
     NSLayoutAttributeNotAnAttribute = 0             //沒有屬性
};
 
NSLayoutAttributeLeading/NSLayoutAttributeTrailing的區別是left/right永遠是指左右,
leading/trailing在某些從右至左習慣的地區(希伯來語等)會變成,leading是右邊,trailing是左邊
 
NSLayoutRelation:
 
typedef  NS_ENUM(NSInteger, NSLayoutRelation) {
     NSLayoutRelationLessThanOrEqual = -1,           //小於等於
     NSLayoutRelationEqual = 0,                      //等於
     NSLayoutRelationGreaterThanOrEqual = 1,         //大於等於
};
 
 
 
需求:button始終處於屏幕中心位置
<demo MainVC>  
 
 
@interface UIView (UIConstraintBasedLayoutInstallingConstraints)

- (NSArray *)constraints NS_AVAILABLE_IOS(6_0);

- (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
- (void)addConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);
- (void)removeConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
- (void)removeConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);
@end
 
 
 
在添加時須要注意的是添加的目標view要遵循如下規則:
 
1.對於兩個同層級view之間的約束關係,添加到他們的父view上
 
2.對於兩個不一樣層級view之間的約束關係,添加到他們最近的共同父view上
 
3.對於有層次關係的兩個view之間的約束關係,添加到層次較高的父view
 
簡單來講,找父類。
 
 
 
<Demo,ButtonVC> 兩個button
 
 
 
 
3.2     使用 Visual Format Language 定義水平和垂直約束
 
Visual Format Language 可視格式語言,用來描述約束條件,這種語言是對視覺描述的一種抽象,大概過程看起來是這樣的:
 
好比兩個按鈕的間距
 
 
 
cancel 與 accept之間水平標準間距 :[cancelButton]-[acceptButton]
 
 
官方文檔:

Visual Format Syntax

The following are examples of constraints you can specify using the visual format. Note how the text visually matches the image.佈局

Standard Space

[button]-[textField]flex

Width Constraint

[button(>=50)]ui

Connection to Superview

|-50-[purpleBox]-50-|spa

Vertical Layout

V:[topField]-10-[bottomField]3d

Flush Views

[maroonView][blueView]code

Priority

[button(100@20)]

Equal Widths

[button1(==button2)]

Multiple Predicates

[flexibleButton(>=70,<=100)]

A Complete Line of Layout

|-[find]-[findNext]-[findField(>=20)]-|

 
 
 
 
例:
 
如圖所示VFL:
email:
H:|-[_textFieldEmail]-|
V:|-100-[_textFieldEmail]
 
confirm email:
H:|-[_textFieldConfirmEmail]-|
V:[_textFieldEmail]-[_textFieldConfirmEmail]
 
register button:
V:[_textFieldConfirmEmail]-[_registerButton]
 
 
 
缺陷:
1.不能表達是一個固定的長寬比,如:imageView.width = 2 * imageView.height
2.UI 組件在 水平方向的居中條件不能由 Visual Format Language 定義
3. ...
 
 
 
方法:
visualFormat:     VFL 可視格式語言
 
 
 
NSLayoutFormatOptions:
 
typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) {
    NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),
    NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),
    NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),
    NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),
    NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),
    NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),
    NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),
    NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline),
   
    NSLayoutFormatAlignmentMask = 0xFFFF,
   
    /* choose only one of these three
     */
    NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default
    NSLayoutFormatDirectionLeftToRight = 1 << 16,
    NSLayoutFormatDirectionRightToLeft = 2 << 16, 
   
    NSLayoutFormatDirectionMask = 0x3 << 16, 
};
 
 
 
注:
在vertical方向 爲 left,right,leading,trailing,centerX
在horizontal方向爲 top,bottom,centerY,baseLine (baseLine即爲文本底部對齊) 
 
 
 
metrics:
[constraints addObjectsFromArray:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-padding-[messageTextView]-kTopAndBottomPadding-|"
    options:NSLayoutFormatAlignAllTop
    metrics:@{@"padding":@5.0}
    views:variableBindings]];
 
views:
A dictionary of views that appear in the visual format string. The keys must be the string values used in the visual format string, and the values must be the view objects.
 
example 1:
V:[_textFieldEmail]-[_textFieldConfirmEmail]
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_textFieldConfirmEmail, _textFieldEmail);
 
example 2:
H:|-[_textFieldEmail]-|
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_textFieldEmail);
 
方法原型:
 
/* This macro is a helper for making view dictionaries for +constraintsWithVisualFormat:options:metrics:views:. 
 NSDictionaryOfVariableBindings(v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v2, @"v2", v3, @"v3", nil];
 */
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
UIKIT_EXTERN NSDictionary *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...) NS_AVAILABLE_IOS(6_0); // not for direct use
 
 
<Demo FirstVC> 兩個textfiled,一個按鈕
 
<Demo ThirdVC>參數:NSLayoutFormatOptions:
 
<Demo ButtonVC> 兩種方法比較
 
<Demo SecondVC>使用不一樣父類的 view 進行約束
 
 
3.3 NSLayoutConstraint 
(過一遍)
 
 
4.XIB 使用 layout
4.1 使用方法
4.2 問題修復
 
 
5.
容易出現的錯誤
 
由於涉及約束問題,所以約束模型下的全部可能出現的問題這裏都會出現,具體來講包括兩種:
 
*)  Ambiguous Layout 佈局不能肯定
*)  Unsatisfiable Constraints 沒法知足約束
 
佈局不能肯定指的是給出的約束條件沒法惟一肯定一種佈局,也即約束條件不足,沒法獲得惟一的佈局結果。這種狀況通常添加一些必要的約束或者調整優先級能夠解決。沒法知足約束的問題來源是有約束條件互相沖突,所以沒法同時知足,須要刪掉一些約束。兩種錯誤在出現時均會致使佈局的不穩定和錯誤,Ambiguous能夠被容忍而且選擇一種可行佈局呈如今UI上,Unsatisfiable的話會沒法獲得UI佈局並報錯。

 

UIView+AutoLayout (GitHub地址:https://github.com/smileyborg/UIView-AutoLayout)

這個第三方寫的類別,將須要代碼建立的視圖的約束進行了一層封裝。若是用代碼設置約束的話,用可視化語言,代碼量少,若是團隊成員都很熟悉的狀況下,可讀性還行!用另外一種方法建立一個個約束,而後加上view上去,雖然可讀性強,可是代碼量大.而這個類別就是將那些方法進行一層可讀性更強的方法,和自動佈局的本意同樣,只要你調用簡單方法去描述各個view之間的關係,而建立的約束的方法,咱們不須要在去關心。做者也增長了一個數組的類別,是爲了方便多個view一塊兒建立約束使用。具體的能夠直接去GitHub上下載,而後查看示例的demo。

相關文章
相關標籤/搜索