iOS開發之 Autolayout 詳解
1. 概述
- Autolayout 是 Apple 自 iOS6 開始引入的旨在解決不同螢幕之間佈局適配的技術
- 蘋果官方推薦開發者使用 Autolayout 進行UI介面的佈局
- Autolayout 有兩個核心概念:1. 參照。 2. 約束
- 使用Autolayout的注意點:
- 新增約束之前需要保證控制元件已被新增到父控制元件中
- 不需要再給View設定frame
- 禁止 autoresizing 功能。
2. 程式碼實現 Autolayout
2.1 步驟:
-
把 View 新增到父控制元件上。
-
新增約束到相應的 View 上。
- (void)addConstraint:(NSLayoutConstraint *)constraint; - (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints; 複製程式碼
-
自動佈局核心公式:
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];
複製程式碼
最終效果:
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];
}
複製程式碼
執行結果: