iOS開發之Masonry框架-使用方法須知

Masonry是一個輕量級的佈局框架,它擁有本身的描述語法(採用更優雅的鏈式語法封裝)來自動佈局,具備很好可讀性且同時支持iOS和Max OS X等。
總之,對於側重寫代碼的coder,請你慢慢忘記Frame,喜歡Masonry吧app

經常使用的屬性與常量

  1. MASViewAttribute 以對應的系統類型框架

    MASViewAttribute NSLayoutAttribute
    view.mas_left NSLayoutAttributeLeft
    view.mas_right NSLayoutAttributeRight
    view.mas_top NSLayoutAttributeTop
    view.mas_bottom NSLayoutAttributeBottom
    view.mas_leading NSLayoutAttributeLeading
    view.mas_trailing NSLayoutAttributeTrailing
    view.mas_width NSLayoutAttributeWidth
    view.mas_height NSLayoutAttributeHeight
    view.mas_centerX NSLayoutAttributeCenterX
    view.mas_centerY NSLayoutAttributeCenterY
    view.mas_baseline NSLayoutAttributeBaseline
  2. UIViewless

    先來一波最爲經常使用的使用方法,你們能夠看一下大體語法,下面會細講使用dom

    //分別設置各個相對邊距(superview爲view的父類視圖,下同)
    make.left.mas_equalTo(superView.mas_left).mas_offset(10);
    make.right.mas_equalTo(superView.mas_right).mas_offset(-10);
    make.top.mas_equalTo(superView.mas_top).mas_offset(10);
    make.bottom.mas_equalTo(superView.mas_bottom).offset(-10);
    
    //直接鏈接使用left大於等於某個值
    make.left.mas_greaterThanOrEqualTo(10);
    
    //設置寬和高
    make.width.mas_equalTo(60);
    make.height.mas_equalTo(60);
    
    //.設置center和寬高比
    make.center.mas_equalTo(superView);
    make.width.mas_equalTo(superView).multipliedBy(1.00/3);
    make.height.mas_equalTo(superView).multipliedBy(0.25);
    
    //.關於約束優先級,此處要注意約束衝突的問題,統一約束優先級大的生效
    make.left.mas_equalTo(100);
    make.left.mas_equalTo(view.superview.mas_left).offset(10);
    make.left.mas_equalTo(20).priority(700);
    make.left.mas_equalTo(40).priorityHigh();
    make.left.mas_equalTo(60).priorityMedium();
    make.left.mas_equalTo(80).priorityLow();
    
    //若是你想讓view的(x座標)左邊大於等於label的左邊,如下兩個約束的寫法效果同樣
    make.left.greaterThanOrEqualTo(label);
    make.left.greaterThanOrEqualTo(label.mas_left);

    注:約束的鏈式寫法中,不包含其餘相對的view時,默認爲其superview,即make.left.mas_equalTo(100);等價於make.left.mas_equalTo(view.superview.mas_left).offset(10);make.left.mas_equalTo(view.superview).offset(10);ide

  3. 更加便利的約束方法佈局

    Masonry提供了一些便利的方法供咱們同時建立多個不一樣的約束,他們被稱爲MASCompositeConstraints,如:動畫

    edges:ui

    // 使一個view的top, left, bottom, right 等於view2的
    make.edges.equalTo(view2);
    
    //相對於superviewde上左下右邊距分別爲5,10,15,20
    make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))

    size:this

    // 使得寬度和高度大於等於 titleLabel
    make.size.greaterThanOrEqualTo(titleLabel)
    
    // 相對於superview寬度大100,高度小50
    make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))

    center:

    //中心與button1對齊
    make.center.equalTo(button1)
    
    //水平方向中心相對向左偏移5,豎直方向中心向下偏移10
    make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))

    你能夠在約束鏈裏添加相應的view來增長代碼的可讀性:

    // 除了top,全部的邊界與superview對齊
    make.left.right.and.bottom.equalTo(superview);
    
    make.top.equalTo(otherView);
  4. NSNumber

    自動佈局容許使用常量去設置寬或高,若是你想經過一個數字設置一個view的最小和最大的width,能夠用equality blocks,以下:

    //width >= 200 && width <= 400
    
    make.width.greaterThanOrEqualTo(@200);
    
    make.width.lessThanOrEqualTo(@400)

    然而自動佈局不容許對齊屬性的約束(如:left,right,centerY等)設置爲常量值,你可使用NSNumber來設置相對於父類view這些約束屬性,如:

    // creates view.left = view.superview.left + 10
    make.left.lessThanOrEqualTo(@10)

    若是你不想使用NSNumber來設置,也能夠用以下結構來建立你的約束,如:

    make.top.mas_equalTo(42);
    
    make.height.mas_equalTo(20);
    
    make.size.mas_equalTo(CGSizeMake(50, 100));
    
    make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
    
    make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
  5. NSArray
    用數組添加集中不一樣類的約束,如:

    make.height.equalTo(@[view1.mas_height, view2.mas_height]);
    
    make.height.equalTo(@[view1, view2]);
    
    make.left.equalTo(@[view1, @100, view3.right]);
  6. 常見約束的各類類型

    /**
     1.尺寸:width、height、size
     2.邊界:left、leading、right、trailing、top、bottom
     3.中心點:center、centerX、centerY
     4.邊界:edges
     5.偏移量:offset、insets、sizeOffset、centerOffset
     6.priority()約束優先級(0~1000),multipler乘因數, dividedBy除因數
     */

Masonry使用注意

  1. 使用mas_makeConstraints方法的元素必須 事先 添加到父元素中,例如[self.view addSubView:view];
  2. mas_equalToequalTo的區別:

      • equalTo:僅支持基本類型;
      • mas_equalTo:支持類型轉換,支持複雜類型。是對equalTo的封裝。支持CGSize CGPoint NSNumber UIEdgeinsets。

      如下實現的是相同的效果:make.width.equalTo(@100);make.width.mas_equalTo(100);

      • mas_equalTo是一個Macro,比較 值;
      • equalTo比較View。

      如下實現的是相同的效果make.bottom.mas_equalTo(ws.view.mas_bottom);make.bottom.equalTo(ws.view);

      mas_equalToequalTo多了類型轉換操做,大多數時候兩個方法是 通用的。可是

      • 對於數值元素使用mas_equalTo
      • 對於對象或多個屬性的處理,使用equalTo;(特別的多個屬性時,必須使用equalTo,例如make.left.and.right.equalTo(self.view)
  3. 去掉mas_前綴,只用equalTo,只須要把下面代碼添加到.prefix文件:

    // 只要添加了這個宏,就不用帶mas_前綴(`equalTo`就等價於`mas_equalTo`)
    #define MAS_SHORTHAND
    
    // 對於默認的約束參數自動裝箱
    #define MAS_SHORTHAND_GLOBALS
    
    // 這個頭文件,必定要放在上面兩個宏的後面
    #import "Masonry.h"
  4. 注意點方法withand,這兩個方法其實沒有作任何操做,方法只是返回對象自己,這個方法的做用,徹底是爲了可讀性。

    make.left.and.right.equalTo(self.view);make.left.right.equalTo(self.view);是徹底同樣的,可是加了and方法的語法,可讀性更好。

  5. multipliedBy的使用只能是設置同一個控件的,好比這裏的bottomInnerViewmake.height.mas_equalTo(bottomInnerView.mas_width).multipliedBy(3);

  6. 簡化:

    [iconView makeConstraints:^(MASConstraintMaker *make){
    make.top.equalTo(self.view).with.offset(30);
    make.left.equalTo(self.view).with.offset(30);
    make.bottom.equalTo(self.view).with.offset(-30);
    make.right.equalTo(self.view).with.offset(-30);
    }]

    能夠簡化爲
    make.top.left.bottom.and.right.equalTo(self.view).with.insets(UIEdgeInsetsMake(10,10,10,10));

    make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30.30.30.30));

  7. 其中leadingleft trailingright在正常狀況下是等價的,可是在一些佈局是從右至左時(好比阿拉伯文),則會對調,因此基本能夠不理不用。用leftright就好。用leadingtrailing後就不要用leftright,若是混用會出現崩潰。

  8. label的約束比必須設置最大的約束寬度;
    self.titleLabel.preferredMaxLayoutWidth = w - 100;

  9. 由於iOS中原點在左上角因此注意使用offset時注意right和bottom用負數

  10. 使用Masonry不須要設置控件的translatesAutoresizingMaskIntoConstraints屬性爲NO,(錯誤觀點:爲防止block中的循環引用,使用弱引用),由於在這裏block是局部的引用,block內部引用self不會形成循環應用的。(不必的寫法:__weak typeof (self) weakSelf = self;

  11. Masonry約束控件出現衝突的問題:當約束衝突發生的時候,咱們能夠設置viewkey來定位是哪一個view。
    好比:

    redView.mas_key = @"redView";
    greenView.mas_key = @"greenView";
    blueView.mas_key = @"blueView";

    若是以爲這樣一個個設置比較繁瑣,Masonry提供了批量設置的宏:

    // 一句代碼便可所有設置
    MASAttachKeysMASAttachKeys(redView,greenView,blueView);

約束的優先級

  1. .priority容許你指定一個精確的優先級,數值越大優先級越高,最高1000
    • priorityHigh等價於UILayoutPriorityDefaultHigh,優先級值爲750
    • priorityMedium介於高優先級和低優先級之間,優先級值在250~750之間。
    • priorityLow等價於UILayoutPriorityDefaultLow,優先級值爲250

優先級能夠在約束的尾部添加:

make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);

Greater/Less通常與Priority一塊兒使用,爲一個Constraint設置了Greater/Less後,調整Priority。若是ConstraintPriority的值越大,程序優先設置它的Constraint效果。

Masonry添加約束的方法

  1. 這個方法只會添加新的約束:

    [view makeConstraints:^(MASConstraintMaker *make) {
    
    }];
  2. 這個方法將會覆蓋之前的某些特定的約束

    [view updateConstraints:^(MASConstraintMaker *make) {
    
    }];
  3. 這個方法會將之前的全部約束刪掉,添加新的約束

    ```
     [view remakeConstraints:^(MASConstraintMaker *make) {
    
     }];
     ```
    • 裏面以爲最好用的是masonry_remakeConstraints,保證不會錯,
  • 要記得將約束寫在updateConstraints裏面:

    - (void) updateConstraints{
    
    }

    而後調用下面這一串:

    [self setNeedsUpdateConstraints];
    
    [self updateConstraintsIfNeeded];
    
    [self layoutIfNeeded];

    不然不會更新。

    • setNeedsLayout:

      1. setNeedsLayout:告知頁面須要刷新,可是不會馬上開始更新。執行後會馬上調用layoutSubviews
      2. layoutIfNeed:告知頁面佈局馬上更新。因此通常都會和setNeedsLayout一塊兒使用。若是但願馬上生成新的frame須要調用此方法。利用這點:通常佈局動畫能夠在更新佈局後直接使用這個方法讓動畫生效。
      3. layoutSubViews:系統重寫佈局setNeedsUpdateConstraints:告訴須要更新約束,可是不會馬上開始。
      4. updateConstraintsIfNeeded:告知馬上更新約束。
      5. updateConstraints:系統更新約束。

修改約束

有時候,你爲了實現動畫或者移除替換一些約束時,你須要去修改一些已經存在的約束,Masonry提供了一些不一樣的方法去更新約束,你也能夠將多個約束存在數組裏。

  1. References

    你能夠持有某個特定的約束,讓其成爲成員變量或者屬性

    //設置爲公共或私接口

    @property (nonatomic, strong) MASConstraint *topConstraint;
    
    ...
    
    // 添加約束
    
    [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);
    
    }];
    
    ...
    
    // 而後能夠調用
    //該約束移除
    [self.topConstraint uninstall];
    //從新設置value,最經常使用
    self.topConstraint.mas_equalTo(20);
    //該約束失效
    [self.topConstraint deactivate];
    //該約束生效
    [self.topConstraint activate];
  2. mas_updateConstraints

    若是你只是想更新一下view對應的約束,可使用 mas_updateConstraints 方法代替 mas_makeConstraints方法

    //這是蘋果推薦的添加或者更新約束的地方

    // 在響應setNeedsUpdateConstraints方法時,這個方法會被調用屢次

    // 此方法會被UIKit內部調用,或者在你觸發約束更新時調用

    - (void)updateConstraints {
    
    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
    
    make.center.equalTo(self);
    
    make.width.equalTo(@(self.buttonSize.width)).priorityLow();
    
    make.height.equalTo(@(self.buttonSize.height)).priorityLow();
    
    make.width.lessThanOrEqualTo(self);
    
    make.height.lessThanOrEqualTo(self);
    
    }];
    
    //調用super
    [super updateConstraints];
    
    }
  3. mas_remakeConstraints

    mas_updateConstraints只是去更新一些約束,然而有些時候修改一些約束值是沒用的,這時候mas_remakeConstraints就能夠派上用場了

    mas_remakeConstraints某些程度類似於mas_updateConstraints,但不一樣於mas_updateConstraints去更新約束值,他會移除以前的view的全部約束,而後再去添加約束

    - (void)changeButtonPosition {
    
      [self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
    
      make.size.equalTo(self.buttonSize);
    
     if (topLeft) {
    
          make.top.and.left.offset(10);
    
     } else {
    
     make.bottom.and.right.offset(-10); 
    
    }
    
    }];
  • 蘋果官方建議:添加/更新約束在updateConstraints這個方法內

    // this is Apple's recommended place for adding/updating constraints
    - (void)updateConstraints {
    //更新約束
    [self.btn updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
    
        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();
    
        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];
    
    //according to apple super should be called at end of method
    //最後必須調用父類的更新約束
    [super updateConstraints];
    }

在哪建立個人約束

貼一個官方說明的例子:

@implementation DIYCustomView
    
- (id)init {
    
    self = [super init];
        
    if (!self) return nil;
        
    // --- Create your views here ---
        
    self.button = [[UIButton alloc] init];
        
    return self;
    
}
    
// tell UIKit that you are using AutoLayout
    
+ (BOOL)requiresConstraintBasedLayout {
    
    return YES;
    
}
    
// this is Apple's recommended place for adding/updating constraints
    
- (void)updateConstraints {
    
    // --- remake/update constraints here
        
    [self.button remakeConstraints:^(MASConstraintMaker *make) {
        
        make.width.equalTo(@(self.buttonSize.width));
            
        make.height.equalTo(@(self.buttonSize.height));
        
    }];
        
    //according to apple super should be called at end of method
        
    [super updateConstraints];
    
}
    
- (void)didTapButton:(UIButton *)button {
    
    // --- Do your changes ie change variables that affect your layout etc ---
        
    self.buttonSize = CGSize(200, 200);
        
    // tell constraints they need updating
        
    [self setNeedsUpdateConstraints];
    
}

建立約束技巧:

  1. 多個(2個以上)控件的等間隔排序顯示

    /**
    *  axisType         軸線方向
    *  fixedSpacing     間隔大小
    *  fixedItemLength  每一個控件的固定長度/寬度
    *  leadSpacing      頭部間隔
    *  tailSpacing      尾部間隔
    *
    */
    
    //1. 等間隔排列 - 多個控件間隔固定,控件長度/寬度變化
    - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
    withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing
    tailSpacing:(CGFloat)tailSpacing;
    
    //2. 等間隔排列 - 多個固定大小固定,間隔空隙變化
    - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
    withFixedItemLength:(CGFloat)fixedItemLength
    leadSpacing:(CGFloat)leadSpacing
    tailSpacing:(CGFloat)tailSpacing;
  2. 多行label的約束問題

    //建立label
        self.label = [UILabel new];
        self.label.numberOfLines = 0;
        self.label.lineBreakMode = NSLineBreakByTruncatingTail;
        self.label.text = @"有的人,沒事時喜歡在朋友圈裏處處點贊,東評論一句西評論一句,比誰都有存在感。等你有事找他了,他就馬上變得很忙,讓你再也找不着。真正的朋友,日常不多聯繫。可一旦你趕上了難處,他會馬上回復你的消息,第一時間站出來幫你。所謂的存在感,不是你有沒有出現,而是你的出現有沒有價值。存在感,不是刷出來的,也不是說出來的。有存在感,未必是要個性鋒芒畢露、甚至鋒利扎人。翩翩君子,溫潤如玉,真正有存在感的人,反而不會刻意去強調他的存在感。他的出現,永遠都恰到好處。我所欣賞的存在感,不是長袖善舞巧言令色,而是對他人的真心關照;不是鋒芒畢露計較勝負,而是讓人相處得舒服;不是時時刻刻聒噪不休,而是關鍵時刻能自告奮勇。別總急着出風頭,但願你能有恰到好處的存在感。";
        [self addSubview: self.label];
    
        [self.label makeConstraints:^(MASConstraintMaker *make) {
            make.left.top.equalTo(10);
            make.right.equalTo(-10);
        }];
    
    //添加約束
    - (void)layoutSubviews {
        //1. 執行 [super layoutSubviews];
        [super layoutSubviews];
    
        //2. 設置preferredMaxLayoutWidth: 多行label約束的完美解決
       self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
    
        //3. 設置preferredLayoutWidth後,須要再次執行 [super layoutSubviews]; 
        //其實在實際中這步不寫,也不會出錯,官方解釋是說設置preferredLayoutWidth後須要從新計算並佈局界面,因此這步最好執行
        [super layoutSubviews];
    }
  3. UIScrollView的問題

    原理同自動佈局同樣 UIScrollView上添加UIView
    UIView上添加須要顯示的控件 UIScrollView滾動高度取決於顯示控件的總高度
    對子控件作好約束,可達到控制UIView的大小

    //建立滾動視圖
        UIScrollView *scrollView = [UIScrollView new];
        self.scrollView = scrollView;
    
        scrollView.backgroundColor = [UIColor grayColor];
        [self addSubview:scrollView];
    
        [self.scrollView makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self);
        }];
    
        [self setUpContentView]; //添加內容視圖
    
    - (void)setUpContentView {
        //約束UIScrollView上contentView
        UIView *contentView = [UIView new];
        [self.scrollView addSubview:contentView];
    
        [contentView makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.scrollView);
            make.width.equalTo(self.scrollView); //此處必填 - 關鍵點
        }];
    
        //添加控件到contentView,約束原理與自動佈局相同
        UIView *lastView;
        CGFloat height = 30;
        for (int i = 0; i <1 5; i ++) {
            UIView *view = UIView.new;
            view.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 256.0  green:arc4random() % 255 / 256.0 blue:arc4random() % 255 / 256.0 alpha:1.0];
            [contentView addSubview:view];
    
            [view makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(lastView ? lastView.bottom : @0);
                make.left.equalTo(0);
                make.width.equalTo(contentView.width);
                make.height.equalTo(height);
            }];
    
            height += 30;
            lastView = view;
        }
    
        [contentView makeConstraints:^(MASConstraintMaker *make) {
            make.bottom.equalTo(lastView.bottom);
        }];
    }
相關文章
相關標籤/搜索