Masonry使用總結

Masonry使用總結


一、Masonry簡介

Masonry是一個輕量級的布局框架,適用于iOS以及OS X。它用簡潔的語法對官方的AutoLayout進行了封裝。 Masonry有它自己的一套框架用來描述NSLayoutConstraints布局的DSL,提高了約束代碼的簡潔性與可讀性。
Masonry現處于bugfix only狀態,將不再有功能性的更新。會有更多的開發者加入Swift陣營,推薦使用Swift寫的Snapkit框架來布局。

snapkit

二、導入Masonry框架

  1. 使用Cocoapods來導入框架,在使用到該框架的文件中添加主頭文件:#import <Masonry/Masonry.h>
  1. 使用直接拖拽的方式拉入框架文件夾,在使用到該框架的文件中添加主頭文件:#import "Masonry.h"

三、Masonry的特性

  1. MASViewAttribute
    |MASViewAttribute |NSLayoutAttribute
    |:----: |:----:
    |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. 簡化前綴的宏定義

  • 為了增加代碼的可讀性這里有兩個簡化代碼的宏:#define MAS_SHORTHAND和#define MAS_SHORTHAND_GLOBALS
  • MAS_SHORTHAND:只要在導入Masonry主頭文件之前定義這個宏, 那么以后在使用Masonry框架中的屬性和方法的時候, 就可以省略mas_前綴
  • MAS_SHORTHAND_GLOBALS:只要在導入Masonry主頭文件之前定義這個宏,那么就可以讓equalTo函數接收基本數據類型, 內部會對基本數據類型進行包裝
    注意:這兩個宏如果想有效使用,必須要在添加Masonry頭文件之前導入進去。在沒有增加宏`MAS_SHORTHAND_GLOBALS時,下面這句是會報錯的。
    make.top.equalTo(42); --> make.top.equalTo([NSNumber numberWithInt:42]);

四、Masonry約束添加步驟

  1. 自定義UIView
  2. 將自定義的UIView添加到父視圖上。
  3. 添加約束

五、Masonry的具體使用

對一個控件添加約束條件要最終滿足可以確定這個空間的大小和位置,否則會報錯缺少約束,當然也要避免約束沖突。下面對View1的左右上下(位置)都進行約束,并沒有對其大小進行約束,但是可根據上述約束自動設置View1的大小。

1. 創建一個View,左右上下空出10個像素

    UIView *view1 = [[UIView alloc]init];
    
    view1.backgroundColor = [UIColor greenColor];
    
    [self.view addSubview:view1];
    
    UIEdgeInsets padding = UIEdgeInsetsMake(150, 30, 30, 30);
    
    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).with.offset(padding.top);
        make.left.equalTo(self.view.mas_left).with.offset(padding.left);
        make.bottom.equalTo(self.view.mas_bottom).with.offset(-padding.bottom);
        make.right.equalTo(self.view.mas_right).with.offset(-padding.right);
    }];
    
    // 一句代碼代替上面的多行
//    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
//        make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(150, 30, 30, 30));
//    }];

2. 使用Priority屬性來做簡單的動畫

.priority allows you to specify an exact priority

.priorityHigh equivalent to UILayoutPriorityDefaultHigh

.priorityMedium is half way between high and low

.priorityLow equivalent to UILayoutPriorityDefaultLow

  • 優先級約束一般放在一個控件約束的最后面,下面看示例。
    // 紅色View
    UIView *redView = [[UIView alloc]init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    // 藍色View
    self.blueView = [[UIView alloc]init];
    self.blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:self.blueView];
    
    // 黃色View
    UIView *yellowView = [[UIView alloc]init];
    yellowView.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:yellowView];
    
    // ---紅色View--- 添加約束
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.view.mas_left).with.offset(20);
        make.bottom.mas_equalTo(self.view.mas_bottom).with.offset(-80);
        make.height.equalTo([NSNumber numberWithInt:50]);
    }];
    
    // ---藍色View--- 添加約束
    [self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(redView.mas_right).with.offset(40);
        make.bottom.width.height.mas_equalTo(redView);
    }];

    // ---黃色View--- 添加約束
    [yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.blueView.mas_right).with.offset(40);
        make.right.mas_equalTo(self.view.mas_right).with.offset(-20);
        make.bottom.width.height.mas_equalTo(redView);
        
        // 優先級設置為250,最高1000(默認)
        make.left.mas_equalTo(redView.mas_right).with.offset(20).priority(250);
    }];
Masonry動畫1
// 點擊屏幕移除藍色View
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.blueView removeFromSuperview];
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];
}
Masonry動畫2

解:這里的三個View的寬度是根據約束自動推斷設置的,對黃色的View設置了一個與紅色View有關的priority(250)的優先級,它同時有對藍色View有個最高的優先級約束(make.left.mas_equalTo(self.blueView.mas_right).with.offset(40);)。當點擊屏幕是,我將藍色View移除,此時第二優先級就是生效。

3. Masonry官方Demo之Basic

basic1
basic2
- (void)testBasic
{
    int padding = 15;
    
    UIView *greenView = [[UIView alloc]init];
    [self.view addSubview:greenView];
    greenView.backgroundColor = [UIColor greenColor];
    
    UIView *redView = [[UIView alloc]init];
    [self.view addSubview:redView];
    redView.backgroundColor = [UIColor redColor];
    
    UIView *blueView = [[UIView alloc]init];
    [self.view addSubview:blueView];
    blueView.backgroundColor = [UIColor blueColor];
    
    // 對 綠色View 進行約束
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.view.mas_left).with.offset(padding); // X
        
        make.top.mas_equalTo(self.view.mas_top).with.offset(padding); // Y
        make.bottom.mas_equalTo(blueView.mas_top).with.offset(-padding);// Y --> 推斷出 Height
        
        make.width.mas_equalTo(redView); // Width == 紅色View(它推斷出Width)
    }];
    
    // 對 紅色View 進行約束
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(greenView.mas_right).with.offset(padding); // X
        make.right.mas_equalTo(self.view.mas_right).with.offset(-padding);// X --> 推斷出 Width
        
        make.bottom.and.height.mas_equalTo(greenView); // Y & Height == 綠色View(它推斷出 Height&Y)
    }];
    
    // 對 藍色View 進行約束
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.view.mas_left).with.offset(padding); // X
        make.right.mas_equalTo(self.view.mas_right).with.offset(-padding); // X --> 推斷出 Width
        
        make.bottom.mas_equalTo(self.view.mas_bottom).with.offset(-padding); // Y
        
        make.height.mas_equalTo(greenView); // 注意1:Height == 綠色View(它推斷出Height)
    }];
}    

總結:對綠色View約束了距離左邊的距離(即確定了X值),約束了距離頂部底部藍色View的距離(這里有個坑需要注意,很多人會認為約束了這兩個就能推斷出Height,其實是錯誤的。除非藍色View添加一個約束讓它們倆高度相同,否則即便知道了它們倆間距和分別對頂部底部的距離,也不知道紅色View藍色View高度如何。)

口訣:誰推斷出大小位置(一或多),與其有約束關系的View的大小位置(一或多)向它看齊。

4. Masonry的更新約束 mas_updateConstraints

Alternatively if you are only updating the constant value of the constraint you can use the convience method mas_updateConstraints instead of mas_makeConstraints

創建一個按鈕,約束好它的位置(居中,寬高等于100且小于屏幕寬高值)。每次點擊一次這個按鈕,其寬高將增大一定的倍數,最終其寬高等于屏幕寬高時將不再變化。

@interface ViewController ()
@property (nonatomic, strong) UIButton *growingButton;
@property (nonatomic, assign) CGFloat scacle;
@end
- (void)createGrowingButton {
    self.growingButton = [UIButton buttonWithType:UIButtonTypeSystem];
    
    [self.growingButton setTitle:@"點我放大" forState:UIControlStateNormal];
    
    self.growingButton.layer.borderColor = UIColor.greenColor.CGColor;
    
    self.growingButton.layer.borderWidth = 3;
    
    [self.growingButton addTarget:self action:@selector(onGrowButtonTaped:) forControlEvents:UIControlEventTouchUpInside];
    
    [self.view addSubview:self.growingButton];
    self.scacle = 1.0;
    
    [self.growingButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(self.view);
        
        // 初始寬、高為100,優先級最低
        make.width.height.mas_equalTo(100 * self.scacle);
        
        // 最大放大到整個view
        make.width.height.lessThanOrEqualTo(self.view);
    }];    
}
- (void)onGrowButtonTaped:(UIButton *)sender {
    self.scacle += 1.0;
    
    // 告訴self.view約束需要更新
    [self.view setNeedsUpdateConstraints];
    
    // 調用此方法告訴self.view檢測是否需要更新約束,若需要則更新,下面添加動畫效果才起作用
    
    [self.view updateConstraintsIfNeeded];
    
    [UIView animateWithDuration:0.3 animations:^{
        [self.view layoutIfNeeded];
    }];
}
#pragma mark - updateViewConstraints
// 重寫該方法來更新約束
- (void)updateViewConstraints {
    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
        // 這里寫需要更新的約束,不用更新的約束將繼續存在
        // 不會被取代,如:其寬高小于屏幕寬高不需要重新再約束
        make.width.height.mas_equalTo(100 * self.scacle);
    }];
    
    [super updateViewConstraints];
}
更新約束

5. Masonry的重寫約束 mas_remakeConstraints

Creates a MASConstraintMaker with the callee view. Any constraints defined are added to the view or the appropriate superview once the block has finished executing. All constraints previously installed for the view will be removed.

創建一個按鈕,約束好其位置(與屏幕上左右的距離為0,與屏幕底部距離為350),點擊按鈕后全屏展現(即與屏幕底部距離為0)。

@property (nonatomic, strong) UIButton *growingButton;
@property (nonatomic, assign) BOOL isExpanded;
- (void)createExpandButton {
    self.isExpanded = NO;
    self.growingButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.growingButton setTitle:@"點我展開" forState:UIControlStateNormal];
    self.growingButton.layer.borderColor = UIColor.greenColor.CGColor;
    self.growingButton.layer.borderWidth = 3;
    self.growingButton.backgroundColor = [UIColor redColor];
    [self.growingButton addTarget:self action:@selector(onGrowButtonTaped:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.growingButton];
    [self.growingButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(0);
        make.left.right.mas_equalTo(0);
        make.bottom.mas_equalTo(-350);
    }];
}
#pragma mark - updateViewConstraints
- (void)updateViewConstraints {
    // 這里使用update也能實現效果
    // remake會將之前的全部移除,然后重新添加
    __weak __typeof(self) weakSelf = self;
    [self.growingButton mas_remakeConstraints:^(MASConstraintMaker *make) {
        // 這里重寫全部約束,之前的約束都將失效
        make.top.mas_equalTo(0);
        make.left.right.mas_equalTo(0);
        if (weakSelf.isExpanded) {
            make.bottom.mas_equalTo(0);
        } else {
            make.bottom.mas_equalTo(-350);
        }
    }];
    [super updateViewConstraints];
}
- (void)onGrowButtonTaped:(UIButton *)sender {
    self.isExpanded = !self.isExpanded;
    if (!self.isExpanded) {
        [self.growingButton setTitle:@"點我展開" forState:UIControlStateNormal];
    } else {
        [self.growingButton setTitle:@"點我收起" forState:UIControlStateNormal];
    }
    // 告訴self.view約束需要更新
    [self.view setNeedsUpdateConstraints];
    // 調用此方法告訴self.view檢測是否需要更新約束,若需要則更新,下面添加動畫效果才起作用
    [self.view updateConstraintsIfNeeded];
    
    [UIView animateWithDuration:0.3 animations:^{
        [self.view layoutIfNeeded];
    }];
}
  • mas_remakeConstraintsmas_updateConstraints 的區別在于前者重新對視圖進行了約束(拋棄了之前的約束),后者是更新約束條件(保留未更新的約束,如:這次更新了對 height 的約束,其他對X&Y以及寬的約束不變)。
重寫約束1
重寫約束2

6. Masonry的比例使用 multipliedBy

使用multipliedBy必須是對同一個控件本身,如果修改成相對于其它控件會出導致Crash。

    UIView *topView = [[UIView alloc]init];
    [topView setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:topView];
    
    UIView *topInnerView = [[UIView alloc]init];
    [topInnerView setBackgroundColor:[UIColor greenColor]];
    [topView addSubview:topInnerView];
    
    UIView *bottomView = [[UIView alloc]init];
    [bottomView setBackgroundColor:[UIColor orangeColor]];
    [self.view addSubview:bottomView];
    
    UIView *bottomInnerView = [[UIView alloc]init];
    [bottomInnerView setBackgroundColor:[UIColor blueColor]];
    [bottomView addSubview:bottomInnerView];
    
    [topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.right.mas_equalTo(0);
        make.height.mas_equalTo(bottomView);
    }];
    
    [topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.mas_equalTo(0);
        make.width.mas_equalTo(topInnerView.mas_height).multipliedBy(3);
        make.center.mas_equalTo(topView);
    }];
    
    [bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.bottom.right.mas_equalTo(0);
        make.height.mas_equalTo(topView);
        make.top.mas_equalTo(topView.mas_bottom);
    }];
    
    [bottomInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.bottom.mas_equalTo(bottomView);
        make.height.mas_equalTo(bottomInnerView.mas_width).multipliedBy(3);
        make.center.mas_equalTo(bottomView);
    }];
multiply
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容