AutoLayout
AutoLayout是基于約束的描述性的布局系統(tǒng)
官方文檔:
概念
AutoLayout的核心觀念就是從基于frame的布局轉(zhuǎn)換到基于約束的布局。
基于frame的布局,frame based layout,根據(jù)相對(duì)坐標(biāo)原點(diǎn)的位置來(lái)布局:
基于約束的布局,Constraint based layout:
約束公式
翻譯過(guò)來(lái)就是item1的某個(gè)屬性和item2的某個(gè)屬性乘以倍數(shù)加常數(shù)有某種關(guān)系
約束屬性
約束分類
按照item數(shù)目:一元約束,二元約束
按照約束屬性的作用:大小約束,位置約束(水平約束,垂直約束)
約束準(zhǔn)則
大小屬性不能約束位置屬性。反之也是
常數(shù)值不能約束位置屬性。
非1倍數(shù)不能作用于位置屬性。
水平屬性不能約束垂直屬性,反之也是
Leading/Trailing屬性不能約束Left/Right屬性
約束關(guān)系
等于,小于或等于,大于或等于
創(chuàng)建約束
?xib創(chuàng)建:
https://github.com/cooop/iOSDemo/tree/master/AutoLayoutDemo
代碼創(chuàng)建:
步驟:創(chuàng)建一個(gè)約束,將約束添加到合適的view上,重復(fù)以上得到一個(gè)可滿足的無(wú)歧義的約束
[NSLayoutConstraint constraintWithItem:redView
????????????????????????????????????????????????????attribute:NSLayoutAttributeLeading
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?relatedBy:NSLayoutRelationEqual
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toItem:blueView
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?attribute:NSLayoutAttributeTrailing
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? multiplier:1.0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?constant:8];
將約束添加到合適的view上
一元約束,放在item上
二元約束,原則找兩個(gè)item的最近公共祖先
API:[view addConstraint:constraint];
[view removeConstraint:constraint];
重復(fù)以上得到一個(gè)可滿足的無(wú)歧義的約束:
AutoLayout的終極目標(biāo)就是得到可滿足無(wú)歧義的布局解決方案,需要item在水平垂直兩個(gè)方向各至少需要兩個(gè)約束
不可滿足的約束,在一個(gè)方向上缺少約束,使得item在改方向上布局不能確定,會(huì)有隱式的布局問(wèn)題
沖突約束,在一個(gè)方向上,item含有兩個(gè)以上約束,且約束不能同時(shí)滿足,系統(tǒng)會(huì)丟棄沖突約束,選擇一個(gè)滿足的解決方案,也會(huì)有隱式的布局問(wèn)題
解決歧義
優(yōu)先級(jí):范圍1-1000,數(shù)值越大優(yōu)先級(jí)越高,優(yōu)先級(jí)高的約束率先滿足。默認(rèn)約束都是Required的(最高優(yōu)先級(jí)),所以不要以為優(yōu)先級(jí)設(shè)為DefaultHight就很高了,還沒(méi)有不設(shè)來(lái)的高,正確的做法是把其他沖突的約束的優(yōu)先級(jí)設(shè)置低。
Required = 1000
DefaultHight = 750
DefaultLow = 250
FittingSizeLevel = 50
constraint.priority = 1000
constraint.priority = UILayoutPriorityRequired
內(nèi)在大小 Intrinsic Content Size
有一些view有一個(gè)內(nèi)在的大小,例如UILabel設(shè)置完字體和文字,就有一個(gè)內(nèi)在寬度和高度恰好包裹文字。UIImage如果有圖片,內(nèi)在寬度和高度與圖片大小一致。
如果有內(nèi)在寬度,在水平方向上可以少設(shè)置一個(gè)約束;如果有內(nèi)在高度,在垂直方向上可以少設(shè)置一個(gè)約束。即UILabel只需要設(shè)置left和top兩個(gè)約束就可以可滿足。
view的content hugging和 compression resistance屬性
content hugging是讓view抱緊,當(dāng)view的寬度約束大于view的內(nèi)在寬度,content hugging優(yōu)先級(jí)設(shè)置的高,view不會(huì)拉伸
compression resistance是讓view頂住,當(dāng)view的寬度約束小于view的內(nèi)在寬度,compression resistance優(yōu)先級(jí)設(shè)置的高。view不會(huì)被壓縮
UIScrollView約束
scrollView和他外部的item之間的約束,約束的是scrollView的frame
scrollView和他內(nèi)部的item之間的edges和margins約束,約束的是scrollView的contentsize
scrollView和他內(nèi)部的item之間的height, width,和centers約束,約束的是scrollView的frame
Visual Format Language
VFL:描述性語(yǔ)言。 H:[blueView]-8-[redView]
API: [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[blueView]-8-[redView]" options:NSLayoutFormatAlignAllTop metrics:nil views:NSDictionaryOfVariableBindings(blueView,redView)]];
缺點(diǎn): 太太太太容易寫(xiě)錯(cuò)了
AutoLayout新特性
iOS 8:
新屬性lastBaseline、firstBaseline、leftMargin、rightMargin、topMargin、bottomMargin、leadingMargin、trailingMargin、centerXMargin、centerYMargin
約束的激活的反激活:
@property (getter=isActive) BOOL active
[NSLayoutConstraint activateConstraints:constraintsArray]
[NSLayoutConstraint deactivateConstraints:constraintsArray]
iOS 9:
約束錨,NSLayoutAnchor,只需考慮約束,不用考慮加在哪個(gè)view上,類似Masonry
[imageView.trailingAnchor constraintEqualToAnchor:label.leadingAnchor constant:20]
UILayoutGuide:專門的輔助布局的控件,不會(huì)加到view上,但是可以“占位置”,省去了加一堆空白view輔助布局的煩惱
UILayoutGuide *space1 = [[UILayoutGuide alloc] init];
[self.view addLayoutGuide:space1];
UILayoutGuide *space2 = [[UILayoutGuide alloc] init];
[self.view addLayoutGuide:space2];
[space1.widthAnchor constraintEqualToAnchor:space2.widthAnchor].active = YES;
[self.saveButton.trailingAnchor constraintEqualToAnchor:space1.leadingAnchor].active = YES;
[self.cancelButton.leadingAnchor constraintEqualToAnchor:space1.trailingAnchor].active = YES;
[self.cancelButton.trailingAnchor constraintEqualToAnchor:space2.leadingAnchor].active = YES;
[self.clearButton.leadingAnchor constraintEqualToAnchor:space2.trailingAnchor].active = YES;
UIStackView:水平或垂直方向上一系列的view,以此排列,解決前一個(gè)view隱藏或刪除,后面的view需要更新約束的尷尬
Masonry
github
https://github.com/SnapKit/Masonry
官方定義:
Masonry is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Masonry has its own layout DSL which provides a chainable way of describing your NSLayoutConstraints which results in layout code that is more concise and readable. Masonry supports iOS and Mac OS X
幾個(gè)點(diǎn):輕量級(jí)、基于AutoLayout、鏈?zhǔn)讲季諨SL、高可讀性、支持iOS和OS X、有Swift版本SnapKithttps://github.com/SnapKit/SnapKit
NSLayoutConstraints的問(wèn)題:
復(fù)雜,可讀性太低
語(yǔ)法
添加約束:
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
????????make.leading.equalTo(blueView.mas_trailing).multipliedBy(1).offset(8);
}];
更改約束:
mas_updateConstraints:注意只能改常數(shù)值
mas_remakeConstraints:刪除之前與view相關(guān)的所有約束重新創(chuàng)建
刪除約束: 需要記錄約束
@property (nonatomic, strong) MASConstraint *topConstraint;
...
// when making constraints
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
? ? ? self.topConstraint =? make.top.equalTo(superview.mas_top).with.offset(padding.top);
? ? ? make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
...
// then later you can call
[self.topConstraint uninstall];
約束
約束屬性:MASViewAttribute
約束關(guān)系:
.equalTo?equivalent to?NSLayoutRelationEqual
.lessThanOrEqualTo?equivalent to?NSLayoutRelationLessThanOrEqual
.greaterThanOrEqualTo?equivalent to?NSLayoutRelationGreaterThanOrEqual
優(yōu)先級(jí):
.priority?allows you to specify an exact priority
.priorityHigh?equivalent to?UILayoutPriorityDefaultHigh
.priorityMedium?is half way between high and low
.priorityLow?equivalent to?UILayoutPriorityDefaultLow
更靈活的語(yǔ)法
簡(jiǎn)化:
make.leading.equalTo(self.view.mas_leading).multipliedBy(1).offset(0);
-----乘數(shù)是1可以省略,常數(shù)是0可以省略----->
make.leading.equalTo(self.view.mas_leading);
-----item1的屬性和item2的屬性相同,item2的屬性可以省略----->
make.leading.equalTo(self.view);
-----item1直接添加在item2上,item2可以省略, 寫(xiě)@0----->
make.leading.equalTo(@(0));
-----equalTo每次常數(shù)值都要寫(xiě)@,好煩,可以用mas_equalTo----->
make.leading.mas_equalTo(0);
合并
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
????????????make.top.equalTo(self.view).offset(10);????
????????????make.left.equalTo(self.view).offset(10);
????????????make.bottom.equalTo(self.view).offset(-10);
????????????make.right.equalTo(self.view).offset(-10);
}];
-----item2和乘數(shù)和常數(shù)一致,可以合并在一行寫(xiě)----->
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
????????make.top.left.equalTo(self.view).offset(10);
????????make.bottom.right.equalTo(self.view).offset(-10);
}];
-----幾個(gè)屬性也可以合在一起----->
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
????????make.edges.equalTo(self.view).insets(padding);
}];
-----item1直接添加在item2上,equalTo和insets可合并----->
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
????????make.edges.mas_equalTo(padding);
}];
合并合并:
height.and.width -->size
centerX.and.centerY --> center
例如:
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
合并合并合并:
item1和item1的屬性一樣,多個(gè)item2也可以合并在一行創(chuàng)建多個(gè)約束,傳入數(shù)組即可
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);
可讀性:
make.top.left.equalTo(self.view).offset(10); --> make.top.and.left.equalTo(self.view).with.offset(10);
and和with并沒(méi)有實(shí)際作用,只是單純返回self,只為了可讀性的考量
創(chuàng)建約束代碼的位置
參考這篇博客:
http://reviewcode.cn/article.html?reviewId=14
View中:直接在init方法里創(chuàng)建.
ViewController中:直接在viewDidLoad()里創(chuàng)建.
何時(shí)更新:需要更新的代碼如果比較少可以就在當(dāng)時(shí)更新,比較多則放在updateConstraints()?,然后在合適的時(shí)候setNeedsUpdateConstraints()?批量更新
補(bǔ)充TableViewCell實(shí)踐經(jīng)驗(yàn):創(chuàng)建時(shí)候在init中創(chuàng)建,在所有subview都加入了contentview之后,調(diào)用自定函數(shù)setupConstraints(),所有創(chuàng)建約束代碼加載此處,可以避免約束創(chuàng)建的類沒(méi)有添加到任何父類引發(fā)的崩潰。所有與model相關(guān)的約束更新放在updateConstraints()中,在bindWIthModel是調(diào)用setNeedsUpdateConstraints()?更新約束。