AutoLayout & Masonry 小結(jié)

AutoLayout

AutoLayout是基于約束的描述性的布局系統(tǒng)

官方文檔:

https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/index.html#//apple_ref/doc/uid/TP40010853-CH7-SW1

概念

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()?更新約束。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容