使用Masonry搭建特殊佈局時與xib的對比

以前只有比較淺的接觸過Masonry。項目中大多數的佈局仍是用xib中的AutoLayout與手碼的frame計算相結合,相信也會有不少項目和我同樣是這兩種佈局的組合。其實xib各方面用的感受都挺好,之前是性能問題,衝突問題飽受人詬病,但隨着蘋果的更新換代這些問題也逐漸趨向最小化。 咱們團隊整改的主要緣由是爲了更細粒度的組件化。由於將一塊代碼複用到另外一個頁面遠比從xib中拖幾個控件到別的頁面來的要快,而且使用Masonry寫出來的代碼在控件間關係上很是清晰便於理解。html

普通的佈局就略過不說了,這裏搭建一些較爲特殊的佈局。組件化

若是你不是在董鉑然博客園看到本文請點擊查看原文佈局

1.下方有三個UIView或UIButton,要隨時隨着屏幕寬度的變化(橫屏豎屏)始終保持等寬等高。

 

上面的右圖是xib下搭建,使用常規的Masonry語法完成佈局的代碼是這樣的性能

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_left).with.offset(0);
        make.bottom.equalTo(self.view.mas_bottom).with.offset(0);
        make.height.equalTo(@100);
    }];
    
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(redView.mas_right).with.offset(0);
        make.bottom.equalTo(self.view.mas_bottom).with.offset(0);
        make.width.equalTo(redView.mas_width).with.offset(0);
        make.height.equalTo(redView.mas_height).with.offset(0);
    }];
    
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(blueView.mas_right).with.offset(0);
        make.bottom.equalTo(self.view.mas_bottom).with.offset(0);
        make.right.equalTo(self.view.mas_right).with.offset(0);
        make.width.equalTo(blueView.mas_width).with.offset(0);
        make.height.equalTo(blueView.mas_height).with.offset(0);
    }];

代碼中能夠看到具體的邏輯仍是特別清晰的。能夠清楚地瞭解各個控件間的關係,基本上每一行表明着xib中的一根約束。字體

而且Masonry支持了一些省略和簡寫:動畫

若是是兩個控件的同一個位置(約束)之間的聯繫,括號中能夠只寫以來的控件約束能夠省略;ui

若是約束依賴的是同一個控件,那能夠用and將兩個約束連在一行寫;spa

若是偏移量offset是0,後面的with.offset(0)能夠省略;3d

將上面的代碼最簡能夠寫成以下htm

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.bottom.equalTo(self.view);
        make.height.equalTo(@100);
    }];
    
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.and.width.and.height.equalTo(redView);
        make.left.equalTo(redView.mas_right);
    }];
    
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.and.width.and.height.equalTo(blueView);
        make.right.equalTo(self.view);
        make.left.equalTo(blueView.mas_right);
    }];

注:對於這種幾個模塊功能類似位置相近的,建議多包一層父控件,便於整個組件的操做與抽離,而且能夠從宏觀的角度看項目結構更加清晰。 

 

2.實現相互依賴的自動佈局,背景View的高度由本身子控件的Label能拉多長決定。

簡單的描述就是,label的左邊和頂部是依賴父控件grayView的。可是父控件grayView的底部是依賴子控件Label的。

上面右圖是IB頁面搭建的AutoLayout,可是按照xib的搭建的思路寫出的代碼是有問題的。

    [grayView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(self.view).with.offset(50);
        make.width.equalTo(@200);
        make.bottom.equalTo(contentLbl).with.offset(10);
    }];
    
    [contentLbl mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(grayView).with.offset(10);
        make.right.equalTo(grayView).with.offset(-10);
    }];

 上面的代碼會致使崩潰,緣由就是上面的第4行依賴的contentLbl的約束尚未搭建因此致使崩潰。在xib中會出現紅色箭頭的狀況,就是約束錯誤,在將約束調整正確後紅色箭頭會變成黃色箭頭,就是約束正確。 可是使用Masonry與xib不一樣,xib出現錯誤能夠容忍你改正,Masonry只要出現錯誤就會立刻崩潰。 所以對於這種相互依賴的約束,下面的還沒創建就要約束他看似沒法完成,實際的解決方案就是用子控件反過來依賴他也是能夠的。 也就是說,你想讓父控件依賴子控件比他多10像素,和設置子控件依賴父控件比他少10像素是一個等價的概念,能夠相互轉化。若是你是細心的網友,你會發如今xib中你雖然設置的是父控件依賴子控件,但實際的約束仍是放在子控件裏的。

修改後的代碼以下:

    [grayView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(self.view).with.offset(50);
        make.width.equalTo(@200);
    }];
    
    [contentLbl mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(grayView).with.offset(10);
        make.right.equalTo(grayView).with.offset(-10);
        make.bottom.equalTo(grayView).with.offset(-10);
    }];

 

3.設置百分比的約束,即一個約束是另外一個約束百分之多少。

這種約束通常適用於width和height。 對於屏幕的適配頗有用,等於把之前算frame的思想加到了autoLayout裏。

    [grayView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(self.view).with.offset(50);
//        make.width.equalTo(@200);
        make.width.equalTo(self.view).multipliedBy(0.5).offset(0);
    }];
    
    [contentLbl mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(grayView).with.offset(10);
        make.right.equalTo(grayView).with.offset(-10);
        make.bottom.equalTo(grayView).with.offset(-10);
    }];

和原生的約束同樣,設置向量係數(比例)以後也能夠再設置一個偏移量。

 

4.設置約束的更新

當前的自動佈局第三方庫有多種,Masonry相比來講也算是重量級的了。Masonry相比其餘自動佈局的庫最大的優勢是在於約束的更新作的很好。

假設如今的約束是上面第2條時的約束,如今設置按鈕點擊更新約束代碼以下

- (IBAction)btnclick:(id)sender {
    [self.grayView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(@300);
    }];

    [UIView animateWithDuration:3.0 animations:^{
        [self.grayView layoutIfNeeded];
    }];
}

若是給約束的變化設置約束,須要用UIView動畫的block將 layoutIfNeed包裹住。而不是直接將約束更新包裹住。

更新約束要注意mas_updateConstraints 和 mas_remakeConstraints 有本質區別。 前者update是保留以前的約束再添加你本次新增的約束。若是出現了相同的設置約束的方法,僅僅是值不一樣,則會直接替換掉原來的約束。  後者remake是直接把原來的所有幹掉,而後添加本次設置的約束。

因此update要注意的是不要用新的方法添加了約束會與以前的約束相沖突。而remake須要注意的是你在remake裏面寫的約束必須是一個完整的約束,由於以前的約束全清空了。

若是沒有徹底弄清兩個方法的區別則可能會寫出如下代碼,這是錯誤的

- (IBAction)btnclick:(id)sender {
    [self.grayView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(self.view).with.offset(10);
        make.width.equalTo(self.view).multipliedBy(0.5);
    }];
    
    [UIView animateWithDuration:3.0 animations:^{
        [self.grayView layoutIfNeeded];
    }];
}

由於原來的make.width.equalTo(@200); 和本次的 make.width.equalTo(self.view).multipliedBy(0.5); 雖然都是設置width可是設置方法不一樣,因此會會新舊都保留致使衝突報錯。

若是你不是在董鉑然博客園看到本文請點擊查看原文

 

5.設置優先級和Label抗壓縮

有的時候Label的寬度或是內部字體發生變化的時候,可能會出現XX... 這種狀況,這是由於Label被壓縮了。Masonry有抗壓縮的設置,而且這個設置和Masonry的優先級priority息息相關。iOS9字體的變化致使不少Label出現... ,若是當初使用Masonry來設置佈局而且設置了抗壓縮,那應該就能夠徹底避免此問題。

設置一個View,裏面有一個子控件Label。

    [blackView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view).with.offset(50);
        make.top.equalTo(self.view).with.offset(300);
        make.size.mas_equalTo(CGSizeMake(200, 50));
    }];
    
    [contentLbl2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(blackView).with.offset(10);
        make.right.equalTo(blackView).with.offset(-50);
        make.height.equalTo(@30);
    }];

上面的第一塊代碼就是搭建一個普通的父控件,能夠無視。下面一塊是Label的約束

若是這麼設置,右邊要比父控件少50,因此Label的文字顯示不下,會被積壓。

 

可是若是加入一個約束:「width最少也大於200」,而且給這個約束設置優先級比 「右邊要比父控件少50」 的優先級高,代碼以下

    [contentLbl2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(blackView).with.offset(10);
        make.width.greaterThanOrEqualTo(@200).priority(900);
        make.right.equalTo(blackView).with.offset(-50).priority(800);
        make.height.equalTo(@30);
    }];

第3行的優先級大於第4行的優先級 因此執行第3行,Label並無被擠壓。

 

抗壓縮還有另外一種設置方法 

    [contentLbl2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(blackView).with.offset(10);
        make.right.equalTo(blackView).with.offset(-50).priority(990);
        make.height.equalTo(@30);
    }];
    
    [contentLbl2 setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];

設置「右邊要比父控件少50」 的優先級爲1000如下, 而後在下面再寫上一行水平方向抗壓縮的代碼。 這裏要注意上面的「右邊比父控件少50」優先級不能不寫,由於Masonry這種類型的代碼默認優先級都是1000,而下面UILayoutPriorityRequired的優先級也是1000,那麼下面的代碼就不起效果了。

 

其實還有更簡單的設置方法,不用寫水平方向抗壓縮,只須要將上面約束後面的優先級改到750如下

    [contentLbl2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.top.equalTo(blackView).with.offset(10);
        make.right.equalTo(blackView).with.offset(-50).priority(749);
        make.height.equalTo(@30);
    }];

由於優先級750對應的優先級枚舉是UILayoutPriorityDefaultHigh,能夠理解成Masonry自己就有一種抗壓縮的保護,優先級是750。

    [contentLbl2 setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];

這行代碼應該是全部label默認存在的。

因此之後若是想讓Label抗壓縮,直接把優先級設置到750如下就OK了。

 

總結:

1.Masonry的方法並很少,鏈式方法,每個點語法都是返回一個約束對象,一個個特定的約束是這個對象的屬性。

2.Masonry不一樣於xib搭建約束出現紅色還能調整,前者一出現問題直接崩潰,將問題卡在開發階段。

3.Masonry創建約束前必須將父子關係搭建好,而且不能對尚未設置約束的控件添加依賴。

4.更新約束要弄清remake和update的區別。

未經受權不得轉載

相關文章
相關標籤/搜索