AutoLayout & Masonry 小結

AutoLayout

AutoLayout是基于約束的描述性的布局系統

官方文檔:

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

概念

AutoLayout的核心觀念就是從基于frame的布局轉換到基于約束的布局。

基于frame的布局,frame based layout,根據相對坐標原點的位置來布局:

基于約束的布局,Constraint based layout:

約束公式

翻譯過來就是item1的某個屬性和item2的某個屬性乘以倍數加常數有某種關系

約束屬性

約束分類

按照item數目:一元約束,二元約束

按照約束屬性的作用:大小約束,位置約束(水平約束,垂直約束)

約束準則

大小屬性不能約束位置屬性。反之也是

常數值不能約束位置屬性。

非1倍數不能作用于位置屬性。

水平屬性不能約束垂直屬性,反之也是

Leading/Trailing屬性不能約束Left/Right屬性

約束關系

等于,小于或等于,大于或等于

創建約束

?xib創建:

https://github.com/cooop/iOSDemo/tree/master/AutoLayoutDemo

代碼創建:

步驟:創建一個約束,將約束添加到合適的view上,重復以上得到一個可滿足的無歧義的約束

[NSLayoutConstraint constraintWithItem:redView

????????????????????????????????????????????????????attribute:NSLayoutAttributeLeading

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?relatedBy:NSLayoutRelationEqual

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toItem:blueView

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?attribute:NSLayoutAttributeTrailing

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? multiplier:1.0

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?constant:8];

將約束添加到合適的view上

一元約束,放在item上

二元約束,原則找兩個item的最近公共祖先

API:[view addConstraint:constraint];

[view removeConstraint:constraint];

重復以上得到一個可滿足的無歧義的約束:

AutoLayout的終極目標就是得到可滿足無歧義的布局解決方案,需要item在水平垂直兩個方向各至少需要兩個約束

不可滿足的約束,在一個方向上缺少約束,使得item在改方向上布局不能確定,會有隱式的布局問題

沖突約束,在一個方向上,item含有兩個以上約束,且約束不能同時滿足,系統會丟棄沖突約束,選擇一個滿足的解決方案,也會有隱式的布局問題

解決歧義

優先級:范圍1-1000,數值越大優先級越高,優先級高的約束率先滿足。默認約束都是Required的(最高優先級),所以不要以為優先級設為DefaultHight就很高了,還沒有不設來的高,正確的做法是把其他沖突的約束的優先級設置低。

Required = 1000

DefaultHight = 750

DefaultLow = 250

FittingSizeLevel = 50

constraint.priority = 1000

constraint.priority = UILayoutPriorityRequired

內在大小 Intrinsic Content Size

有一些view有一個內在的大小,例如UILabel設置完字體和文字,就有一個內在寬度和高度恰好包裹文字。UIImage如果有圖片,內在寬度和高度與圖片大小一致。

如果有內在寬度,在水平方向上可以少設置一個約束;如果有內在高度,在垂直方向上可以少設置一個約束。即UILabel只需要設置left和top兩個約束就可以可滿足。

view的content hugging和 compression resistance屬性

content hugging是讓view抱緊,當view的寬度約束大于view的內在寬度,content hugging優先級設置的高,view不會拉伸

compression resistance是讓view頂住,當view的寬度約束小于view的內在寬度,compression resistance優先級設置的高。view不會被壓縮

UIScrollView約束

scrollView和他外部的item之間的約束,約束的是scrollView的frame

scrollView和他內部的item之間的edges和margins約束,約束的是scrollView的contentsize

scrollView和他內部的item之間的height, width,和centers約束,約束的是scrollView的frame

Visual Format Language

VFL:描述性語言。 H:[blueView]-8-[redView]

API: [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[blueView]-8-[redView]" options:NSLayoutFormatAlignAllTop metrics:nil views:NSDictionaryOfVariableBindings(blueView,redView)]];

缺點: 太太太太容易寫錯了

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,只需考慮約束,不用考慮加在哪個view上,類似Masonry

[imageView.trailingAnchor constraintEqualToAnchor:label.leadingAnchor constant:20]

UILayoutGuide:專門的輔助布局的控件,不會加到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,以此排列,解決前一個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

幾個點:輕量級、基于AutoLayout、鏈式布局DSL、高可讀性、支持iOS和OS X、有Swift版本SnapKithttps://github.com/SnapKit/SnapKit

NSLayoutConstraints的問題:

復雜,可讀性太低

語法

添加約束:

[redView mas_makeConstraints:^(MASConstraintMaker *make) {

????????make.leading.equalTo(blueView.mas_trailing).multipliedBy(1).offset(8);

}];

更改約束:

mas_updateConstraints:注意只能改常數值

mas_remakeConstraints:刪除之前與view相關的所有約束重新創建

刪除約束: 需要記錄約束

@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

約束關系:

.equalTo?equivalent to?NSLayoutRelationEqual

.lessThanOrEqualTo?equivalent to?NSLayoutRelationLessThanOrEqual

.greaterThanOrEqualTo?equivalent to?NSLayoutRelationGreaterThanOrEqual

優先級:

.priority?allows you to specify an exact priority

.priorityHigh?equivalent to?UILayoutPriorityDefaultHigh

.priorityMedium?is half way between high and low

.priorityLow?equivalent to?UILayoutPriorityDefaultLow

更靈活的語法

簡化:

make.leading.equalTo(self.view.mas_leading).multipliedBy(1).offset(0);

-----乘數是1可以省略,常數是0可以省略----->

make.leading.equalTo(self.view.mas_leading);

-----item1的屬性和item2的屬性相同,item2的屬性可以省略----->

make.leading.equalTo(self.view);

-----item1直接添加在item2上,item2可以省略, 寫@0----->

make.leading.equalTo(@(0));

-----equalTo每次常數值都要寫@,好煩,可以用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和乘數和常數一致,可以合并在一行寫----->

[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {

????????make.top.left.equalTo(self.view).offset(10);

????????make.bottom.right.equalTo(self.view).offset(-10);

}];

-----幾個屬性也可以合在一起----->

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的屬性一樣,多個item2也可以合并在一行創建多個約束,傳入數組即可

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并沒有實際作用,只是單純返回self,只為了可讀性的考量

創建約束代碼的位置

參考這篇博客:

http://reviewcode.cn/article.html?reviewId=14

View中:直接在init方法里創建.

ViewController中:直接在viewDidLoad()里創建.

何時更新:需要更新的代碼如果比較少可以就在當時更新,比較多則放在updateConstraints()?,然后在合適的時候setNeedsUpdateConstraints()?批量更新

補充TableViewCell實踐經驗:創建時候在init中創建,在所有subview都加入了contentview之后,調用自定函數setupConstraints(),所有創建約束代碼加載此處,可以避免約束創建的類沒有添加到任何父類引發的崩潰。所有與model相關的約束更新放在updateConstraints()中,在bindWIthModel是調用setNeedsUpdateConstraints()?更新約束。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370