iOS:Masonry約束經驗(19-03-21更)


1、label約束:

  1)、只需約束x、y 點相關就行。寬高 長度相關不用約束,就算用boundingRectWithSize計算出來的,也可能不准。

    如:top、bottom二選一,trailing、leading二選一,或者center,寬高會自動生成。(同時約束trailing、leading的話,相當於設了寬度)

  2)、有些地方怕label過長超出,或覆蓋其他控件,這時就需要約束 寬高,讓其“...”。

    后續補充:還是沒必要約束寬,可以讓top、bottom、trailing、leading設置 lessThanOrEqualTo 屬性。

         小於正常顯示,大於會省略號,比起 equalTo 寬,寫死的,會好用些

         比如高寫死的話,會發現,字體垂直居中,這樣就和左邊的“標題Label”,水平不對齊了

         1)、lessThanOrEqualTo:小於或等於(注意負號)( label 有個最大寬度屬性 preferredMaxLayoutWidth,有點像? )

         2)、equalTo:等價於

         3)、greaterThanOrEqualTo:大於或等於(注意負號)

   3)、垂直約束,我更喜歡用centerY。

    用top的話:1)、UI的字體大小和iOS可能會有偏差,多行label,誤差就越大。而設中心點,他是中心定點,上下伸縮。

            2)、如果又有一個控件,如ImageView,跟label的Y軸中心對齊,而label,用top對齊其他且當前label.text = nil,會出錯(好像此時label的center會等於top)。

 

2、在cell重用里刷新數據,要用更新約束:mas_updateConstraints 或 重新約束:mas_remakeConstraints。

  后續補充:3種常用

    1)、mas_makeConstraints:約束

    2)、mas_updateConstraints:更新相同的約束對象數據,如寬。

    3)、mas_remakeConstraints:重新約束所有

  再補充:也沒必要一直在cell里改約束。比如,cell里經常修改一個label的約束的時候,

    1)、可以考慮多弄幾個樣式的label,當前不需要的label隱藏。

    2)、可以通過 安裝、卸載 。在初始化的時候,定好約束優先級。

 

3、UIPageControl小圓點、UISwitch開關,約束 center/ top、bottom / trailing、leading 等x、y 點即可。

 

4、約束centerX/centerY,容易犯的錯

make.centerX.mas_equalTo(10.0);
make.centerX.offset(10.0);

  語句看起來,像是給centerX = 10.0的數值,然而效果卻是父視圖的centerX + 10.0,

  原因,參照下面的附錄,它自動默認一樣的屬性。應該寫詳細點。

make.centerX.mas_equalTo(self.view.mas_leading).offset(10.0);

  再補充:如果1個固定的控件和1個移動的控件(下划線)centerX,約束一樣,如,make.centerX.mas_equalTo(固定.mas_centerX),

      接着,想通過mas_updata,更新centerX到另一個控件,會發現約束沖突,連固定的控件一起移動。

      解決,取另一個參照物。

        1)、newCenterX = 另一個控件.centerX 。

        2)、make.centerX.mas_equalTo(固定.mas_leading).offset(newCenterX)

 

5、屬性

  1)、之前用得較多的:top、bottom、trailing、leading、center、width、height

  2)、現在開始用的 edges == top、bottom、trailing、leading。

             insets 在 邊界約束好的基礎上,設置與父視圖的間隙,為正值!!也可以直接在edges里設置,一樣。

           size == width、height。

           sizeOffset 在 size 約束好的基礎上,進行增+減-。主要用於相對另一個View。

           multipliedBy 倍數,比如0.5、1.5。不局限在相同屬性,也可以設置為自身 width 為 height 的3倍。

  3)、center 配合 size 不錯。就像layer的 position 和 bound 。

 

6、父視圖contentView的寬高,有時候可以不用設置,它會根據子類去約束,比如移除某個子View,自動縮小、變化。

  同時,也要注意因為父視圖缺少約束,而造成的“自動變化Bug”。

 

 

7、優先級 與 動畫( .priority(500) 或 UILayoutPriority 常量 )

  在同一個控件做約束,可以設置兩個相同屬性的約束,對其優先級進行設置,會先找優先級高的約束。

  優先級高約束有問題,會再向下尋找次之優先級的屬性約束。

  默認優先級 UILayoutPriorityRequired == 1000

    后續補充:同一屬性、同一優先級,多個約束也沒問題。不過都得是greaterThanOrEqualTo、lessThanOrEqualTo的約束。

         使用參照“8、優先級與邊界”。

    再補充:比如網絡請求,加載到數據,給當前內容為nil的控件寫入數據的時候(例如,當前 空的昵稱label、空的性別圖片 ,沒顯示出來)

        多寫一行 UIView animation + layoutIfNeeded ,有意想不到的效果。  

    對 再補充 補充:使用 UIView animation + layoutIfNeeded 做動畫的情況下,

            可使用 [self.animationView.layer removeAllAnimations]; 移除動畫,如需可用 block 的 finished 判斷是否完成動畫

[UIView animateWithDuration:8.0
                      delay:0.0
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     // 動畫
                     [self layoutIfNeeded];
                 } completion:^(BOOL finished) {
                     // 動畫完成
                     [self.animationView mas_updateConstraints:^(MASConstraintMaker *make) {
                         make.leading.mas_equalTo(SCREEN_WIDTH);
                     }];
                 }];

      再再補充:做彈窗動畫,如果初始化為約束,做動畫也用 約束+ UIView animation + layoutIfNeeded,在第一次(懶)加載的時候會導致所有的控件一起動畫,

         可先在初始化的時候 [self setNeedsLayout] + [self layoutIfNeeded] ,完成UI布局。

 

  1)、如 移除優先級高的參照物,再layout。就可以做動畫。

    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.leading.equalTo(參照物1.mas_trailing).offset(20).priority(750);
        make.leading.equalTo(參照物2.mas_trailing).offset(20).priority(250);
    }];

    [參照物1 removeFromSuperview];
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];

  2)、或者提取出來

    2-1)、卸載掉該約束

    MASConstraint *leadingMas;

    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        leadingMas = make.leading.equalTo(參照物1.mas_trailing).offset(20).priority(750);
        make.leading.equalTo(參照物2.mas_trailing).offset(20).priority(250);
    }];

    [leadingMas uninstall]; 
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];

  后續補充:卸載uninstall、安裝install 可以重復。只要變量沒釋放

    2-2)、修改約束。

    MASConstraint *widthMas;

    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        widthMas = make.width.equalTo(50);
    }];

    widthMas.equalTo(100); 
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];

  3)、也可以用 mas_updateConstraints 對同一屬性進行更新

    [view mas_updateConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(100);
    }];

    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];    

 

8、優先級與邊界

  1)、初始化

@property (nonatomic,strong) MASConstraint *topMas;
@property (nonatomic,strong) MASConstraint *leadingMas;

[self.moveView mas_makeConstraints:^(MASConstraintMaker *make) {
    // 邊界。不寫優先級,默認優先級最高 = UILayoutPriorityRequired = 1000
    make.leading.top.greaterThanOrEqualTo(self.bgView);
    make.trailing.bottom.lessThanOrEqualTo(self.bgView);
    // 確定寬高
    make.width.mas_equalTo(50);
    make.height.mas_equalTo(50);
    // 確定位置。高優先級,可變動的位置。
    self.topMas = make.top.equalTo(self.bgView).offset(50).priority(750);
    self.leadingMas = make.leading.equalTo(self.bgView).offset(50).priority(750);
}];

  后續補充:寬高看情況判斷是否需要約束。

  2)、改變位置

    2-1)、改動 make 返回的數據

self.topMas.offset(100);
self.leadingMas.offset(100);

    2-2)、直接更新約束

[self.moveView mas_updateConstraints:^(MASConstraintMaker *make) {
    make.top.mas_equalTo(self.bgView).offset(100).priority(750);
    make.leading.mas_equalTo(self.bgView).offset(100).priority(750);
}];

    補充,2-1)比 2-2)需要多存2個變量,且只能修改offset,無法改之前的參照物、優先級。

              但是,寫法更簡潔,適合改動少的。

       優先級,可以自定宏,也可以用系統給 UILayoutPriorityDefaultHigh = 750 、UILayoutPriorityDefaultLow = 250。

    再補充:邊界,也可以在拖動的時候判斷x、y、x+width、y+height,純手工計算。

 

9、如果兩個並排的lable。

  1)、在字數(寬高)可能相互擠壓的時候,可以優先保護某個Label的完整性

// 水平位置,優先級高
[label1 setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];

// 水平位置,優先級低(會被擠壓)
[label2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];

  后續補充:setContentCompressionResistancePriority 為UIView的方法,設置抗壓性 優先級,第二個參數可選水平、垂直方向。

       CompressionResistance 抗壓越高,則越不會被擠壓,如lable被擠壓,會顯示“...”。

       相對的,有個 setContentHuggingPriority ,擁抱優先級,沒用過,應該是優先級越高,越不會被拉伸之類的把?!

  2)、字體較少的時候,較空的地方可以用 lessThanOrEqualTo 。留空。

 

10、一些只要約束 位置 ,自動生成長寬的控件,如label,可以重寫 intrinsicContentSize ,添加內邊距,增大。

- (CGSize)intrinsicContentSize
{
    CGSize tempSize = [super intrinsicContentSize];
    return CGSizeMake(tempSize.width + insets.left + insets.right, tempSize.height + insets.top + insets.bottom);
}

 

11、自動頂住 導航欄或狀態欄 , tabbar或底部

// self.mas_topLayoutGuide;    
// self.mas_bottomLayoutGuide;

// self.mas_topLayoutGuideTop;
// self.mas_topLayoutGuideBottom;

// self.mas_bottomLayoutGuideTop;
// self.mas_bottomLayoutGuideBottom;

make.top.mas_equalTo(self.mas_topLayoutGuide);

 

12、baseLine。

不大熟悉。好像主要應用於,對齊兩個View里的子Lable,從而約束到View的位置。

重寫、以返回要對齊的baseView

iOS(6.0 - 9.0)

- (UIView *)viewForBaselineLayout {
    return baseView;
}

iOS(9.0 - )

- (UIView *)viewForFirstBaselineLayout{
    return baseView;
}

- (UIView *)viewForLastBaselineLayout{
    return baseView;
}

 

13、scrollView約束

scrollView,可視范圍正常約束,如:  

[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.mas_equalTo(self.view);
}];

contentView,約束,如下(高度可變):

[scrollView addSubview:contentView];
[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    make.width.equalTo(scrollView);
    make.height.greaterThanOrEqualTo(@(0.f));
}];

需要的items,添加、約束到contentView上。

后續補充:忘了寫了,最后一個item的bottom要和contentView的bottom約束上,contentView才有確定的高

 

14、獲取上面剛約束完的尺寸,來設置別的view.frame。要先layout,否則可能獲取到{0, 0, 0, 0}。

[self layoutIfNeeded];

 

 

 

附錄:

例、兩個並排的按鈕

//equalTo 比較 View,
//mas_equalTo 比較 數值
//如果在 redButton 里約束有用到 greenButton ,則 greenButton 在之前就要添加到和 redButton 同一父類View下。

紅色的按鈕(equalTo)
UIButton *redButton = [[UIButton alloc]init];
redButton.backgroundColor = [UIColor redColor];
[self.view addSubview:redButton];
[redButton mas_makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@100);
    make.leading.equalTo(self.view).offset(10.0);
    make.bottom.equalTo(self.view).offset(-10.0);
}];
    
綠色的按鈕(mas_equalTo)
1)、比較全的寫法
UIButton *greenButton = [[UIButton alloc]init];
greenButton.backgroundColor = [UIColor greenColor];
[self.view addSubview:greenButton];
[greenButton mas_makeConstraints:^(MASConstraintMaker *make) {
    make.height.mas_equalTo(100);
    make.width.mas_equalTo(redButton.mas_width);
    make.trailing.mas_equalTo(self.view.mas_trailing).offset(-10.0);
    make.bottom.mas_equalTo(self.view.mas_bottom).offset(-10.0);
    make.leading.mas_equalTo(redButton.mas_trailing).offset(10.0);
}];

2)、如果屬性是一樣的,可以省略后面的屬性
make.width.mas_equalTo(redButton);
make.trailing.mas_equalTo(self.view).offset(-10.0);
make.bottom.mas_equalTo(self.view).offset(-10.0);

3)、相對於(2),甚至可以寫0,相對父視圖。寬可不能跟着寫0,寬0就0了
make.trailing.mas_equalTo(0).offset(-10.0);
make.bottom.mas_equalTo(0).offset(-10.0);

4)、相對於(3),既然都可以0,那干脆直接去掉
make.trailing.offset(-10.0);
make.bottom.offset(-10.0);

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM