iOS開發之 Autolayout 詳解

極客學偉發表於2018-05-26

2018-05-24-1_HemKn1OC2bh7tUpK7_p0Ng

iOS開發之 Autolayout 詳解

1. 概述

  1. Autolayout 是 Apple 自 iOS6 開始引入的旨在解決不同螢幕之間佈局適配的技術
  2. 蘋果官方推薦開發者使用 Autolayout 進行UI介面的佈局
  3. Autolayout 有兩個核心概念:1. 參照。 2. 約束
  4. 使用Autolayout的注意點:
    1. 新增約束之前需要保證控制元件已被新增到父控制元件中
    2. 不需要再給View設定frame
    3. 禁止 autoresizing 功能。

2. 程式碼實現 Autolayout

2.1 步驟:

  1. 把 View 新增到父控制元件上。

  2. 新增約束到相應的 View 上。

    - (void)addConstraint:(NSLayoutConstraint *)constraint;
    - (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints;
    複製程式碼
  3. 自動佈局核心公式: obj1.property1 =(obj2.property2 * multiplier)+ constant value

NSLayoutConstraint
/**
view1 :要約束的控制元件
attr1 :約束的型別(做怎樣的約束)
relation :與參照控制元件之間的關係
view2 :參照的控制元件
attr2 :約束的型別(做怎樣的約束)
multiplier :乘數
c :常量
*/
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
複製程式碼

2.2 例項

新增一個左間距100 上間距200,寬150 高64 的紅檢視:
- (void)testAutolayout1 {
    UIView *redV = [[UIView alloc] init];
    redV.backgroundColor = [UIColor redColor];
    redV.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redV];
    /**
     view1 :要約束的控制元件
     attr1 :約束的型別(做怎樣的約束)
     relation :與參照控制元件之間的關係
     view2 :參照的控制元件
     attr2 :約束的型別(做怎樣的約束)
     multiplier :乘數
     c :常量
     */
    /// 左間距100:
    NSLayoutConstraint *consLeft = [NSLayoutConstraint constraintWithItem:redV attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:100];
    [self.view addConstraint:consLeft];
    
    /// 上間距200:
    NSLayoutConstraint *consTop = [NSLayoutConstraint constraintWithItem:redV attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:200];
    [self.view addConstraint:consTop];
    
    /// 寬150:
    NSLayoutConstraint *consWidth = [NSLayoutConstraint constraintWithItem:redV attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:150];
    [redV addConstraint:consWidth];
    
    /// 高64:
    NSLayoutConstraint *consHeight = [NSLayoutConstraint constraintWithItem:redV attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:64];
    [redV addConstraint:consHeight];
}
複製程式碼
在上檢視基礎上新增一個與紅檢視右間距相同,高度相同,頂部距離紅色檢視間距20,寬度為紅色檢視一半的藍色View
 UIView *blueV = [[UIView alloc] init];
    blueV.backgroundColor = [UIColor blueColor];
    blueV.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueV];
    
    /// 和 redV 右間距為0
    NSLayoutConstraint *b_consRight = [NSLayoutConstraint constraintWithItem:blueV attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:redV attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0];
    [self.view addConstraint:b_consRight];
    
    /// 和 redV 等高
    NSLayoutConstraint *b_consHeight = [NSLayoutConstraint constraintWithItem:blueV attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:redV attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0];
    [self.view addConstraint:b_consHeight];
    
    /// 寬度是 redV 的一半
    NSLayoutConstraint *b_consWidth = [NSLayoutConstraint constraintWithItem:blueV attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redV attribute:NSLayoutAttributeWidth multiplier:0.5 constant:0.0];
    [self.view addConstraint:b_consWidth];
    
    /// 頂部距離 redV 20
    NSLayoutConstraint *b_consTop = [NSLayoutConstraint constraintWithItem:blueV attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:redV attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20.0];
    [self.view addConstraint:b_consTop];
複製程式碼

最終效果:

螢幕快照 2018-05-24 下午3.46.26

2.3 新增約束的規則

在建立約束之後,需要將其新增到作用的view上。 在新增時要注意目標view需要遵循以下規則:

#####(1)對於兩個同層級view之間的約束關係,新增到它們的父view上 #####(2)對於兩個不同層級view之間的約束關係,新增到他們最近的共同父view上 #####(3)對於有層次關係的兩個view之間的約束關係,新增到層次較高的父view上

3. VFL

VFL全稱是Visual Format Language,翻譯過來是“視覺化格式語言”,是蘋果公司為了簡化Autolayout的編碼而推出的抽象語言。

    /*
     format :VFL語句
     opts :約束型別
     metrics :VFL語句中用到的具體數值
     views :VFL語句中用到的控制元件
     */
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *,id> *)views;
複製程式碼

@{@"redV" : redV} 等價於 NSDictionaryOfVariableBindings(redV)

NSDictionary *views =

NSDictionaryOfVariableBindings(blueView, redView);

NSArray *conts2 =

[NSLayoutConstraint constraintsWithVisualFormat:

@"V:[blueView(==blueHeight)]-margin-|" options:0 metrics:

@{@"blueHeight" : @40, @"margin" : @20} views:views];
複製程式碼

約束格式說明:

水平方向        H: 垂直方向        V: Views         [view] SuperView      | 關係         >=,==,<= 空間,間隙       - 優先順序        @value

3.1 VFL 部分語法:

H:|-100-[redV(200)]-|

水平方向距離左邊距100,寬度200

V:|-200-[redV(64)]-|

垂直方向距離頂部200,高度64

H:[redV(72)]-12-[blueV(50)]

水平方向redV 寬度72,blueV 寬度50,他們之間間距12

H:[redV(>=60@700)]

水平方向redV寬度大於等於60,優先順序為700 (優先順序最大1000)

V:[redBox]-[yellowBox(==redBox)]

豎直方向上,先有一個redBox,其下方緊接一個高度等於redBox高度的yellowBox

H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|

水平方向上,Find距離父view左邊緣預設間隔寬度,之後是FindNext距離Find間隔預設寬度;再之後是寬度不小於20的FindField,它和FindNext以及父view右邊緣的間距都是預設寬度。(豎線“|” 表示superview的邊緣)

3.2 VFL的語法

  • 標準間隔:[button]-[textField]
  • 寬約束:[button(>=50)]
  • 與父檢視的關係:|-50-[purpleBox]-50-|
  • 垂直佈局:V:[topField]-10-[bottomField]
  • Flush Views:[maroonView][buleView]
  • 權重:[button(100@20)]
  • 等寬:[button(==button2)]
  • Multiple Predicates:[flexibleButton(>=70,<=100)]

注意事項

建立這種字串時需要注意一下幾點:

  • H:和V:每次都使用一個。
  • 檢視變數名出現在方括號中,例如[view]。
  • 字串中順序是按照從頂到底,從左到右
  • 檢視間隔以數字常量出現,例如-10-。
  • |表示父檢視

3.3 使用Auto Layout時需要注意的點

  • 注意禁用Autoresizing Masks。對於每個需要使用Auto Layout的檢視需要呼叫setTranslatesAutoresizingMaskIntoConstraints:NO
  • VFL語句裡不能包含空格和>,<這樣的約束
  • 佈局原理是由外向里布局,最先螢幕尺寸,再一層一層往裡決定各個元素大小。
  • 刪除檢視時直接使用removeConstraint和removeConstraints時需要注意這樣刪除是沒法刪除檢視不支援的約束導致view中還包含著那個約束(使用第三方庫時需要特別注意下)。解決這個的辦法就是新增約束時用一個區域性變數儲存下,刪除時進行比較刪掉和先前那個,還有個辦法就是設定標記,constraint.identifier = @“What you want to call”。

3.4 佈局約束規則

表達佈局約束的規則可以使用一些簡單的數學術語,如下表

型別 描述
屬性 檢視位置 NSLayoutAttributeLeft, NSLayoutAttributeRight, NSLayoutAttributeTop, NSLayoutAttributeBottom
屬性 檢視前面後面 NSLayoutAttributeLeading, NSLayoutAttributeTrailing
屬性 檢視的寬度和高度 NSLayoutAttributeWidth, NSLayoutAttributeHeight
屬性 檢視中心 NSLayoutAttributeCenterX, NSLayoutAttributeCenterY
屬性 檢視的基線,在檢視底部上方放置文字的地方 NSLayoutAttributeBaseline
屬性 佔位符,在與另一個約束的關係中沒有用到某個屬性時可以使用佔位符 NSLayoutAttributeNotAnAttribute
關係 允許將屬性通過等式和不等式相互關聯 NSLayoutRelationLessThanOrEqual, NSLayoutRelationEqual, NSLayoutRelationGreaterThanOrEqual
數學運算 每個約束的乘數和相加性常數 CGFloat值

3.5 View的改變會呼叫哪些方法

  • 改變frame.origin不會掉用layoutSubviews
  • 改變frame.size會使 superVIew的layoutSubviews呼叫和自己view的layoutSubviews方法
  • 改變bounds.origin和bounds.size都會呼叫superView和自己view的layoutSubviews方法

3.6 VFL 例項:

- (void)testVFL {
    UIView *redV = [[UIView alloc] init];
    redV.backgroundColor = [UIColor redColor];
    redV.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redV];
    
    UIView *blueV = [[UIView alloc] init];
    blueV.backgroundColor = [UIColor blueColor];
    blueV.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueV];
    /*
     format :VFL語句
     opts :約束型別
     metrics :VFL語句中用到的具體數值
     views :VFL語句中用到的控制元件
     */
    //水平方向 redV 左右間距為20
    NSArray *cons1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[redV]-20-|" options:0 metrics:nil views:@{@"redV":redV}];
    [self.view addConstraints:cons1];
    
    //垂直方法redV距離頂部 100, redV 高度為64, blueV頂部距離redV 100 畫素, blueV的高度等於redV
    NSArray *cons2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-margin-[redV(64)]-margin-[blueV(==redV)]" options:NSLayoutFormatAlignAllRight metrics:@{@"margin" : @100} views:NSDictionaryOfVariableBindings(redV,blueV)];
    [self.view addConstraints:cons2];
    
    NSLayoutConstraint *cons = [NSLayoutConstraint constraintWithItem:blueV attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redV attribute:NSLayoutAttributeWidth multiplier:0.5 constant:0.0];
    [self.view addConstraint:cons];
}
複製程式碼

執行結果:

2018-05-24_3.46.26

文中演示Demo 均已開源在 GitHub上,這是連結: XWAutolayoutDemo In Github

相關文章