iOS開發屏幕適配篇——masonry簡介

1、爲何要學masonry(why)?git

目前iOS開發中大多數頁面都已經開始使用Interface Builder的方式進行UI開發了,可是在一些變化比較複雜的頁面,仍是須要經過代碼來進行UI開發的。
並且有不少比較老的項目,自己就還在採用純代碼的方式進行開發。
而如今iPhone和iPad屏幕尺寸愈來愈多,雖然開發者只須要根據屏幕點進行開發,而不須要基於像素點進行UI開發。但若是在項目中根據不一樣屏幕尺寸進行各類判斷,
寫死座標的話,這樣開發起來是很吃力的。
因此通常用純代碼開發UI的話,通常都是配合一些自動化佈局的框架進行屏幕適配。蘋果爲咱們提供的適配框架有:VFL、UIViewAutoresizing、Auto Layout、
Size Classes等。
其中Auto Layout是使用頻率最高的佈局框架,可是其也有弊端。就是在使用UILayoutConstraint的時候,會發現代碼量不少,並且大多都是重複性的代碼,
以致於好多人都不想用這個框架。
後來Github上的出現了基於UILayoutConstraint封裝的第三方佈局框架Masonry,Masonry使用起來很是方便,本篇文章就詳細講一下Masonry的使用。

2、masonry是什麼(what)?github

Masonry是一個對系統NSLayoutConstraint進行封裝的第三方自動佈局框架,採用鏈式編程的方式提供給開發者API。系統AutoLayout支持的操做,Masonry都支持,
相比系統API功能來講,Masonry是有過之而無不及。
Masonry採起了鏈式編程的方式,代碼理解起來很是清晰易懂,並且寫完以後代碼量看起來很是少。以前用NSLayoutConstraint寫不少代碼才能實現的佈局,
用Masonry最少一行代碼就能夠搞定。下面看到Masonry的代碼就會發現,太簡單易懂了。
Masonry是同時支持Mac和iOS兩個平臺的,在這兩個平臺上均可以使用Masonry進行自動佈局。咱們能夠從MASUtilities.h文件中,看到下面的定義,
這就是Masonry經過宏定義的方式,區分兩個平臺獨有的一些關鍵字。
支持的屬性:
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;

 

  

 

3、怎樣學習masonry(how)?編程

 1.masonry遇到的坑app

在使用Masonry進行約束時,不用設置weak控制器self,由於masonry的內容使用的不是block,不存在循環引用。
在使用Masonry進行約束時,有一些是須要注意的。
在使用Masonry添加約束以前,須要在addSubview以後才能使用,不然會致使崩潰。
在添加約束時初學者常常會出現一些錯誤,約束出現問題的緣由通常就是兩種:約束衝突和缺乏約束。對於這兩種問題,能夠經過調試和log排查。
以前使用Interface Builder添加約束,若是約束有錯誤直接就能夠看出來,而且會以紅色或者黃色警告體現出來。而Masonry則不會直觀的體現出來,而是以運行過程當中崩潰或者打印異常log體現,因此這也是手寫代碼進行AutoLayout的一個缺點。
這個問題只能經過多敲代碼,積攢純代碼進行AutoLayout的經驗,慢慢就用起來愈來愈駕輕就熟了。

 Masonry 設置UILabel時多行必定要設置寬度。框架

2.基礎APIless

mas_makeConstraints()    添加約束
mas_remakeConstraints()  移除以前的約束,從新添加新的約束
mas_updateConstraints()  更新約束

equalTo()       參數是對象類型,通常是視圖對象或者mas_width這樣的座標系對象
mas_equalTo()   和上面功能相同,參數能夠傳遞基礎數據類型對象,能夠理解爲比上面的API更強大

width()         用來表示寬度,例如表明view的寬度
mas_width()     用來獲取寬度的值。和上面的區別在於,一個表明某個座標系對象,一個用來獲取座標系對象的值

3.修飾語句dom

Auto Boxing
上面例如equalTo或者width這樣的,有時候須要涉及到使用mas_前綴,這在開發中須要注意做區分。
若是在當前類引入#import "Masonry.h"以前,用下面兩種宏定義聲明一下,就不須要區分mas_前綴。
// 定義這個常量,就能夠不用在開發過程當中使用"mas_"前綴。
#define MAS_SHORTHAND
// 定義這個常量,就可讓Masonry幫咱們自動把基礎數據類型的數據,自動裝箱爲對象類型。
#define MAS_SHORTHAND_GLOBALS
修飾語句
Masonry爲了讓代碼使用和閱讀更容易理解,因此直接經過點語法就能夠調用,還添加了and和with兩個方法。這兩個方法內部實際上什麼都沒幹,
只是在內部將self直接返回,功能就是爲了更加方便閱讀,對代碼執行沒有實際做用。 例以下面的例子: make.top.and.bottom.equalTo(self.containerView).with.offset(padding);

3.經常使用的方法iphone

設置內邊距

/** 
 設置yellow視圖和self.view等大,而且有10的內邊距。
 注意根據UIView的座標系,下面right和bottom進行了取反。因此不能寫成下面這樣,不然right、bottom這兩個方向會出現問題。
 make.edges.equalTo(self.view).with.offset(10);

 除了下面例子中的offset()方法,還有針對不一樣座標系的centerOffset()、sizeOffset()、valueOffset()之類的方法。
 */
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(self.view).with.offset(10);
    make.top.equalTo(self.view).with.offset(10);
    make.right.equalTo(self.view).with.offset(-10);
    make.bottom.equalTo(self.view).with.offset(-10);
}];
經過insets簡化設置內邊距的方式

// 下面的方法和上面例子等價,區別在於使用insets()方法。
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
    // 下、右不須要寫負號,insets方法中已經爲咱們作了取反的操做了。
    make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
}];

4.經常使用例子ide

  4.1 上下排列佈局

    [self.view addSubview:self.blueView];
    [self.view addSubview:self.yellowView];
    [self.view addSubview:self.redView];

    //三個等高視圖佔一個屏幕,關鍵點inses內邊距和高度能夠同時等同於多個視圖make.height.equalTo(@[self.redView,self.blueView]);
    //上下排列核心思想:起點redView視圖固定上左右底,中間blueView固定左右底,底部yellowView固定 左右底,高度等高
1.基本用法

    CGFloat padding = 10;
    [self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.equalTo(self.view).insets(UIEdgeInsetsMake(padding, padding, 0, padding));
        make.bottom.equalTo(self.blueView.mas_top).offset(-10);
    }];
    [self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, 0, padding));
        make.bottom.equalTo(self.yellowView.mas_top).offset(-10);
    }];
    [self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.bottom.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, padding, padding));
        make.height.equalTo(@[self.redView,self.blueView]);
    }];

#pragma mark - 🔒private
- (NSString *)ranomText{
    
    CGFloat length = arc4random()%50 +5;
    NSMutableString *str = [[NSMutableString alloc] init];
    for (NSInteger i = 0; i <length; i++) {
        [str appendString:@"測試一下啦!"];
    }
    return str;
}
- (UIView *)blueView{
    if (!_blueView) {
        _blueView = [[UIView alloc] init];
        [_blueView setBackgroundColor:[UIColor blueColor]];
      
    }
    return _blueView;
}
- (UIView *)yellowView{
    if (!_yellowView) {
        _yellowView = [[UIView alloc] init];
        [_yellowView setBackgroundColor:[UIColor yellowColor]];
        
    }
    return _yellowView;
}
- (UIView *)redView{
    if (!_redView) {
        _redView = [[UIView alloc] init];
        [_redView setBackgroundColor:[UIColor redColor]];
        
    }
    return _redView;
}

 4.2 左右排列

   //左左排列核心思想:起點blueView固定中心、左右,高度及等寬,中間redView固定高度,中心及右邊,最右邊視圖等高,右,中心位置
    CGFloat padding = 10.0f;
    [self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_left).offset(padding);
        make.centerY.equalTo(self.view.mas_centerY);
        make.height.equalTo(@50);
        make.width.equalTo(@[self.redView,self.yellowView]);
        make.right.equalTo(self.redView.mas_left).with.offset(-padding);
    }];
    [self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(@50);
        make.right.equalTo(self.yellowView.mas_left).offset(-padding);
        make.centerY.equalTo(self.view.mas_centerY);
    }];
    [self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(@[self.blueView,self.redView]);
        make.height.equalTo(@[self.blueView,self.redView]);
        
        make.right.equalTo(self.view.mas_right).offset(-10);
    }];

 4.3 上下複合排列

    //左右下核心思想,首先整體結構是上下因此要設置等高,而後再設置上下左右的間隙,左1設置上左下右及高度,右1設置右下上就能夠由於左邊已經有約束,下blueView只須要設置左右下及高度
    CGFloat padding = 10.0f;
    [self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.equalTo(self.view).insets(UIEdgeInsetsMake(padding, padding, 0, 0));
        make.right.equalTo(self.redView.mas_left).offset(-padding);
        make.bottom.equalTo(self.blueView.mas_top).offset(-padding);
        make.width.equalTo(self.redView.mas_width);
    }];
    [self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.right.equalTo(self.view).insets(UIEdgeInsetsMake(padding, 0,0,padding));
        make.bottom.equalTo(self.blueView.mas_top).offset(-padding);
    }];
    [self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, 0, padding));
        make.bottom.equalTo(self.view.mas_bottom);
        make.height.equalTo(self.yellowView.mas_height);
    }];

 4.4 約束比例, multipliedBy 只能設置同一控制的,如自身高度等於寬度的3倍,寬度*這個比例

 UIView *topView = [[UIView alloc] init];
    topView.backgroundColor = [UIColor redColor];
    [self.view addSubview:topView];
    
    UIView *topInnerView = [[UIView alloc] init];
    topInnerView.backgroundColor = [UIColor greenColor];
    [topView addSubview:topInnerView];
    
    UIView *bottomView = [[UIView alloc] init];
    bottomView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:bottomView];
    
    UIView *bottomInnerView = [[UIView alloc] init];
    bottomInnerView.backgroundColor = [UIColor blueColor];
    [bottomView addSubview:bottomInnerView];
    
    [topView mas_makeConstraints:^(MASConstraintMaker *make) {~~
        make.left.right.top.equalTo(self.view).width.offset(0);
        make.height.mas_equalTo(bottomView);
    }];
    [topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(topView);
     
        make.width.equalTo(topInnerView.mas_height).multipliedBy(3);
           make.center.equalTo(topView);
        //設置優先級,設置優先級時必定要加(),優先級 Masonry會優先實現優先級高的設定,發生衝突時,放棄優先級低的設定.
        make.width.height.mas_equalTo(topView).priorityLow();
        make.width.height.lessThanOrEqualTo(topView);
    }];
    [bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.bottom.equalTo(self.view);
        make.top.equalTo(topView.mas_bottom);
    
    }];
    [bottomInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.bottom.mas_equalTo(bottomView);
        make.center.equalTo(bottomView);
        make.height.equalTo(bottomInnerView.mas_width).multipliedBy(3);
        //設置優先級,lessThanOrEqualTo小於等於,優先級 Masonry會優先實現優先級高的設定,發生衝突時,放棄優先級低的設定.
        make.height.width.equalTo(bottomView).priorityLow();
        make.height.width.lessThanOrEqualTo(bottomView);
        
    }];

  4.5 點擊放大,更新約束的使用

/**3.點擊放大,知識點,更新約束,優先級,小於等於等
     核心1://初始化寬度,高爲100,優先級最低
     make.width.height.mas_equalTo(100*self.scale).priorityLow();
     //最大放大到整個view,小於等於view的寬度和高度
     make.width.height.lessThanOrEqualTo(self.view);
     核心2:更新約束 ,告訴須要更新,檢查是否須要更新,當即更新*/
@property (nonatomic, strong) UIButton *bigButton;
@property (nonatomic, assign) CGFloat scale;//比例

    self.scale = 1.0;
    self.bigButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.bigButton setTitle:@"點我放大" forState:UIControlStateNormal];
    self.bigButton.layer.borderColor = UIColor.greenColor.CGColor;
    self.bigButton.layer.borderWidth = 3;
    [self.bigButton addTarget:self action:@selector(onGrowButtonTaped:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.bigButton];
    
    [self.bigButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(self.view);
        //初始化寬度,高爲100,優先級最低
        make.width.height.mas_equalTo(100*self.scale).priorityLow();
        //最大放大到整個view
        make.width.height.lessThanOrEqualTo(self.view);
    }];
#pragma mark - 🎬event response
- (void)onGrowButtonTaped:(UIButton *)sender{
    self.scale += 0.2;
    //告訴self.view約束須要更新
    [self.view setNeedsUpdateConstraints];
    // 調用此方法告訴self.view檢測是否須要更新約束,若須要則更新,下面添加動畫效果才起做用
    [self.view updateConstraintsIfNeeded];
    [UIView animateWithDuration:0.3 animations:^{
        [self.view layoutIfNeeded]; //當即更新
    }];
}
#pragma mark - updateViewConstraints
- (void)updateViewConstraints{
    
    [self.bigButton  mas_updateConstraints:^(MASConstraintMaker *make)  {
        make.center.equalTo(self.view);
        
        //設置優先級
        make.width.height.mas_equalTo(100*self.scale).priorityLow();
        make.width.height.lessThanOrEqualTo(self.view);
    }];
    //調用父類更新約束
    [super updateViewConstraints];
}

  

 4.6 scrollView的簡單適配

   /**4. scrollView,核心思想就是先固定四周,而後再設置contentSize,在這裏是底部
     核心1:preferredMaxLayoutWidth 最大的適配寬度
     核心2:scrollView的適配,首先先固定,而後設置底部的值 
     核心3:配置label頂部間距
     */
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.pagingEnabled = NO;
    [self.view addSubview:self.scrollView];
    [self.scrollView setBackgroundColor:[UIColor lightGrayColor]];
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    UILabel *lastLabel = nil;
    for (NSInteger i = 0; i < 20; i++) {
        UILabel *label = [[UILabel alloc] init];
        label.numberOfLines = 0;
        label.layer.borderColor = [UIColor greenColor].CGColor;
        label.layer.borderWidth = 2.0;
        label.text = [self ranomText];
        label.preferredMaxLayoutWidth = screenWidth - 30;
        label.textAlignment = NSTextAlignmentLeft;
        [self.scrollView addSubview:label];
        
        [label mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.view.mas_left).offset(15);
            make.right.equalTo(self.view.mas_right).offset(-15);
            if (lastLabel) {//下一行是第一行最後一個label加上必定的間距
                make.top.mas_equalTo(lastLabel.mas_bottom).offset(20);
            }else{
                make.top.mas_equalTo(self.scrollView.mas_top).offset(20);
            }
            
        }];
        lastLabel = label;
    }
    [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
        make.bottom.equalTo(lastLabel.mas_bottom).offset(20);
    }];
#pragma mark - 🔒private
- (NSString *)ranomText{
    
    CGFloat length = arc4random()%50 +5;
    NSMutableString *str = [[NSMutableString alloc] init];
    for (NSInteger i = 0; i <length; i++) {
        [str appendString:@"測試一下啦!"];
    }
    return str;
}  
相關文章
相關標籤/搜索