Masonry——簡(jiǎn)化自動(dòng)布局代碼框架

注:以下內(nèi)容來(lái)源于官方源碼、 README 文檔、測(cè)試 Demo或個(gè)人使用總結(jié) !

Masonry 是一個(gè)輕量級(jí)的布局框架,擁有自己的描述語(yǔ)法,采用優(yōu)雅的鏈?zhǔn)秸Z(yǔ)法來(lái)封裝自動(dòng)布局,簡(jiǎn)潔明了并具有高可讀性。

Masonry 利用簡(jiǎn)化、可鏈接和表達(dá)式的語(yǔ)法發(fā)揮 Auto LayoutNSLayoutConstraints 的力量。支持 iOS 和 Mac OS 系統(tǒng)的自動(dòng)布局 。

總的來(lái)說(shuō),就是當(dāng)你在 Interface Bulider 上使用 Auto Layout 的時(shí)候是非常方便的。但是,當(dāng)我們的視圖通過(guò)手寫(xiě)代碼生成時(shí),或是UI界面需要隨用戶(hù)交互而更改,這時(shí)候使用原生的 NSLayoutConstraints 類(lèi)實(shí)現(xiàn)自動(dòng)布局及其繁瑣,Masonry 用簡(jiǎn)潔的語(yǔ)法對(duì) Auto Layout 進(jìn)行了封裝,使用相對(duì)更方便。

NSLayoutConstraints 的問(wèn)題

添加一個(gè)子視圖,跟隨父視圖的大小變化,但是與父視圖邊距有10個(gè)點(diǎn)的距離:


使用 NSLayoutConstraints

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIView *superview = self.view;
    UIView *view1 = [[UIView alloc] init];
    // 設(shè)置為NO,表示通過(guò)添加自己的約束以使用 Auto Layout 定位視圖
    view1.translatesAutoresizingMaskIntoConstraints = NO;
    view1.backgroundColor = [UIColor greenColor];
    [superview addSubview:view1];
    
    // 指定每個(gè)邊緣的插入量(正),值可以是負(fù)的到“起點(diǎn)”
    UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
    
    [superview addConstraints:@[
                                
        // view1 constraints
        // view1.attribute1 = multiplier × superview.attribute2 + constant
        // view1的頂部距離父視圖的頂部10px
        [NSLayoutConstraint constraintWithItem:view1
                                     attribute:NSLayoutAttributeTop
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:superview
                                     attribute:NSLayoutAttributeTop
                                    multiplier:1.0
                                      constant:padding.top],
        // 左邊距離10px
        [NSLayoutConstraint constraintWithItem:view1
                                     attribute:NSLayoutAttributeLeft
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:superview
                                     attribute:NSLayoutAttributeLeft
                                    multiplier:1.0
                                      constant:padding.left],
        // 底部距離10px
        [NSLayoutConstraint constraintWithItem:view1
                                     attribute:NSLayoutAttributeBottom
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:superview
                                     attribute:NSLayoutAttributeBottom
                                    multiplier:1.0
                                      constant:-padding.bottom],
        // 右邊距離10px
        [NSLayoutConstraint constraintWithItem:view1
                                     attribute:NSLayoutAttributeRight
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:superview
                                     attribute:NSLayoutAttributeRight
                                    multiplier:1
                                      constant:-padding.right],
        ]];
}

使用 Masonry

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top);
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

更短的寫(xiě)法

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

Masonry 實(shí)踐

下面參考Masonry介紹與使用實(shí)踐(快速上手Autolayout) @里脊串的開(kāi)發(fā)隨筆,練習(xí)使用Masonry。

1. 居中顯示一個(gè) 300*300 的 superview

UIView *superview = [UIView new];
[superview showPlaceHolder];    // 需要導(dǎo)入MMPlaceHolder框架
superview.backgroundColor = [UIColor blackColor];
[self.view addSubview:superview];

[superview mas_makeConstraints:^(MASConstraintMaker *make) {
    make.center.equalTo(self.view);
    make.size.mas_equalTo(CGSizeMake(300, 300));
}];

2. 讓 superview1 略小于其 superview (邊距為20)

UIView *superview1 =[UIView new];
superview1.backgroundColor = [UIColor redColor];
[superview addSubview:superview1];
[superview1 mas_makeConstraints:^(MASConstraintMaker *make) {
    // 寫(xiě)法一
   make.edges.equalTo(superview).with.insets(UIEdgeInsetsMake(20, 20, 20, 20));

    /** 寫(xiě)法二
    make.top.equalTo(superview).with.offset(20);
    make.left.equalTo(superview).with.offset(20);
    make.bottom.equalTo(superview).with.offset(-20);
    make.right.equalTo(superview).with.offset(-20);
    **/

    /** 寫(xiě)法三
     make.top.left.bottom.and.right.equalTo(superview).with.insets(UIEdgeInsetsMake(20, 20, 20, 20));
    **/
}];

3. 讓兩個(gè)高度為 150 的視圖垂直居中、等寬、等間隔排列 (間隔為10 ),自動(dòng)計(jì)算其寬度

UIView *superview2 =[UIView new];
superview2.backgroundColor = [UIColor orangeColor];
[superview addSubview:superview2];

UIView *superview3 =[UIView new];
superview3.backgroundColor = [UIColor orangeColor];
[superview addSubview:superview3];

int padding1 = 10;
[superview2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(superview.mas_centerY);
    make.left.equalTo(superview.mas_left).with.offset(padding1);
    make.right.equalTo(superview3.mas_left).with.offset(-padding1);
    make.height.mas_equalTo(@150);
    make.width.equalTo(superview3);
}];

[superview3 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(superview.mas_centerY);
    make.left.equalTo(superview2.mas_right).with.offset(padding1);
    make.right.equalTo(superview.mas_right).with.offset(-padding1);
    make.height.mas_equalTo(@150);
    make.width.equalTo(superview2);
}];

4. 在UIScrollView 上順序排列一些view并自動(dòng)計(jì)算contentSize

UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor whiteColor];
[superview addSubview:scrollView];
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(UIEdgeInsetsMake(5, 5, 5, 5));
}];

UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    make.width.equalTo(scrollView);
}];

int count = 10;
UIView *lastView = nil;

for (int i = 1;  i <= count; i++) {
    UIView *subView = [UIView new];
    [container addSubview:subView];
    subView.backgroundColor = [UIColor colorWithHue:(arc4random() % 256 / 256.0)
                                         saturation:(arc4random() % 128 / 256.0) + 0.5
                                         brightness:(arc4random() % 128 / 256.0) + 0.5
                                              alpha:1];

    [subView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.right.equalTo(container);
        make.height.mas_equalTo(@(20 * i)); // 每個(gè)子視圖的高度是20的倍數(shù)

        if (lastView) {
            // 下一個(gè)子視圖的頂部 = 上一個(gè)子視圖的底部
            make.top.mas_equalTo(lastView.mas_bottom);
        }else {
            // 第一次執(zhí)行,lastView = nil,因此第一個(gè)子視圖的頂部是容器的頂部
            make.top.mas_equalTo(container.mas_top);
        }
    }];
    lastView = subView;
}
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    // 容器的底部是最后一個(gè)視圖的底部
    make.bottom.equalTo(lastView.mas_bottom);
}];

5. 橫向或者縱向等間隙的排列一組view

11/15 添加
均勻分布一組view推薦使用 NSArray+MASAdditions.h 類(lèi)中的兩個(gè)方法:

/**
 *  distribute with fixed spacing
 *
 *  @param axisType     which axis to distribute items along
 *  @param fixedSpacing the spacing between each item
 *  @param leadSpacing  the spacing before the first item and the container
 *  @param tailSpacing  the spacing after the last item and the container
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

/**
 *  distribute with fixed item size
 *
 *  @param axisType        which axis to distribute items along
 *  @param fixedItemLength the fixed length of each item
 *  @param leadSpacing     the spacing before the first item and the container
 *  @param tailSpacing     the spacing after the last item and the container
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

需要使用封裝的 category 類(lèi):

  • UIView+LJC.h
#import <UIKit/UIKit.h>

/**
 橫向或者縱向等間隙的排列一組view
 */
@interface UIView (LJC)

- (void) distributeSpacingHorizontallyWith:(NSArray*)views;

- (void) distributeSpacingVerticallyWith:(NSArray*)views;

@end
  • UIView+LJC.m
#import "UIView+LJC.h"
#import <Masonry/Masonry.h>


@implementation UIView (LJC)


/**
 橫向排列一組view

 @param views view數(shù)組
 */
- (void) distributeSpacingHorizontallyWith:(NSArray*)views
{
    NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
    
    for ( int i = 0 ; i < views.count+1 ; ++i )
    {
        UIView *v = [UIView new];
        [spaces addObject:v];
        [self addSubview:v];
        
        [v mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(v.mas_height);
        }];
    }
    
    UIView *v0 = spaces[0];
    
    [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.mas_left);
        make.centerY.equalTo(((UIView*)views[0]).mas_centerY);
    }];
    
    UIView *lastSpace = v0;
    for ( int i = 0 ; i < views.count; ++i )
    {
        UIView *obj = views[i];
        UIView *space = spaces[i+1];
        
        [obj mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(lastSpace.mas_right);
        }];
        
        [space mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(obj.mas_right);
            make.centerY.equalTo(obj.mas_centerY);
            make.width.equalTo(v0);
        }];
        
        lastSpace = space;
    }
    
    [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.mas_right);
    }];
    
}


/**
 垂直排列一組view

 @param views view數(shù)組
 */
- (void) distributeSpacingVerticallyWith:(NSArray*)views
{
    NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
    
    for ( int i = 0 ; i < views.count+1 ; ++i )
    {
        UIView *v = [UIView new];
        [spaces addObject:v];
        [self addSubview:v];
        
        [v mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(v.mas_height);
        }];
    }
    
    
    UIView *v0 = spaces[0];
    
    [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.mas_top);
        make.centerX.equalTo(((UIView*)views[0]).mas_centerX);
    }];
    
    UIView *lastSpace = v0;
    for ( int i = 0 ; i < views.count; ++i )
    {
        UIView *obj = views[i];
        UIView *space = spaces[i+1];
        
        [obj mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(lastSpace.mas_bottom);
        }];
        
        [space mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(obj.mas_bottom);
            make.centerX.equalTo(obj.mas_centerX);
            make.height.equalTo(v0);
        }];
        
        lastSpace = space;
    }
    
    [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(self.mas_bottom);
    }];
    
}

@end
  • 實(shí)例測(cè)試:
UIView *subView11 = [UIView new];
UIView *subView12 = [UIView new];
UIView *subView13 = [UIView new];
UIView *subView21 = [UIView new];
UIView *subView31 = [UIView new];

subView11.backgroundColor = [UIColor redColor];
subView12.backgroundColor = [UIColor redColor];
subView13.backgroundColor = [UIColor redColor];
subView21.backgroundColor = [UIColor redColor];
subView31.backgroundColor = [UIColor redColor];

[superview addSubview:subView11];
[superview addSubview:subView12];
[superview addSubview:subView13];
[superview addSubview:subView21];
[superview addSubview:subView31];

//給予不同的大小,測(cè)試結(jié)果

[subView11 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.equalTo(@[subView12,subView13]); // 11、12、13 Y方向?qū)R
    make.centerX.equalTo(@[subView21,subView31]); // 11、21、31 X方向?qū)R
    make.size.mas_equalTo(CGSizeMake(40, 40));
}];

[subView12 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(30, 30));
}];

[subView13 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(20, 20));
}];

[subView21 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(50, 50));
}];

[subView31 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(60, 60));
}];

[superview distributeSpacingHorizontallyWith:@[subView11,subView12,subView13]];
[superview distributeSpacingVerticallyWith:@[subView11,subView21,subView31]];

[superview showPlaceHolderWithAllSubviews];
[superview hidePlaceHolder];

使用空白的占位view填充目標(biāo)view的旁邊

繼續(xù)參考:Masonry使用總結(jié),學(xué)習(xí)使用 Masonry;

6. Masonry 中的比例 multipliedBy

使用 multipliedBy 屬性必須是對(duì)同一個(gè)控件本身,如果修改成相對(duì)于其它控件會(huì)導(dǎo)致 Crash。

UIView *topView = [UIView new];
[topView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:topView];

UIView *topInnerView = [UIView new];
[topInnerView setBackgroundColor:[UIColor greenColor]];
[topInnerView showPlaceHolder];
[self.view addSubview:topInnerView];

UIView *bottomView =[UIView new];
[bottomView setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:bottomView];

UIView *bottomInnerView =[UIView new];
[bottomInnerView setBackgroundColor:[UIColor blackColor]];
[bottomInnerView showPlaceHolder];
[self.view addSubview:bottomInnerView];

[topView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.left.and.right.mas_equalTo(0);
    make.height.mas_equalTo(bottomView);
}];

[topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.and.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.and.right.mas_equalTo(0);
    make.height.mas_equalTo(topView);
    make.top.mas_equalTo(topView.mas_bottom);
}];

[bottomInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.and.bottom.mas_equalTo(bottomView);
    make.height.mas_equalTo(bottomInnerView.mas_width).multipliedBy(3);
    make.center.mas_equalTo(bottomView);
}];

7. 使用 priority 優(yōu)先級(jí)屬性來(lái)做簡(jiǎn)單的動(dòng)畫(huà)

.priority allows you to specify an exact priority

.priorityHigh equivalent to UILayoutPriorityDefaultHigh

.priorityMedium is half way between high and low

.priorityLow equivalent to UILayoutPriorityDefaultLow

  1. 先為blueView設(shè)置屬性:
@interface ViewController ()

@property (nonatomic,strong) UIView *blueView;

@end

2.創(chuàng)建視圖并設(shè)置約束:

// 紅色視圖
UIView *redView = [UIView new];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];

// 藍(lán)色視圖
self.blueView = [UIView new];
self.blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:self.blueView];

// 黃色視圖
UIView *yellowView = [UIView new];
yellowView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:yellowView];

// ————紅色視圖約束————
[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.mas_equalTo(50);
}];

// ————藍(lán)色視圖約束————
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.mas_equalTo(redView.mas_right).with.offset(40);
    make.bottom.width.and.height.with.height.mas_equalTo(redView);
}];

// ————黃色視圖約束————
[yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
    // 默認(rèn)滿(mǎn)足高優(yōu)先級(jí)約束
    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.and.height.mas_equalTo(redView);

    //** priority設(shè)置為250,最高1000(默認(rèn))**
    make.left.mas_equalTo(redView.mas_right).with.offset(20).priority(250);
}];

3.點(diǎn)擊屏幕移除藍(lán)色視圖方法:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.blueView removeFromSuperview];
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];
}

注釋?zhuān)狐S色視圖的left屬性分別設(shè)置了兩個(gè)約束,一個(gè)相對(duì)于藍(lán)色視圖的高優(yōu)先級(jí)約束:make.left.mas_equalTo(self.blueView.mas_right).with.offset(40);

另一個(gè)相對(duì)于紅色視圖的低優(yōu)先級(jí)約束:make.left.mas_equalTo(redView.mas_right).with.offset(20).priority(250);

當(dāng)藍(lán)色視圖存在時(shí),兩個(gè)約束同時(shí)存在,為避免約束沖突,系統(tǒng)默認(rèn)滿(mǎn)足高優(yōu)先級(jí)的約束;

當(dāng)點(diǎn)擊屏幕,藍(lán)色視圖被移除后,低優(yōu)先級(jí)的約束就會(huì)生效。

8.Masonry更新約束方法:mas_updateConstraints

如果你只想更新約束的常量值,則可以使用更方便的方法:mas_updateConstraints而不是mas_makeConstraints

  1. 創(chuàng)建一個(gè) Button 屬性、scale 屬性用于記錄放大比例:

    @interface ViewController ()
    @property (nonatomic,strong) UIButton *growingButton;
    @property (nonatomic,assign) CGFloat scale;
    @end
    
  1. 創(chuàng)建 Button 實(shí)例:

    self.growingButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.growingButton setTitle:@"點(diǎn)我放大" 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.scale = 1.0;
    
    [self.growingButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(self.view);
        // 初始寬、高為100,優(yōu)先級(jí)最低
        make.width.height.mas_equalTo(100 * self.scale);
        // 最大放大到整個(gè)view
        make.width.and.height.lessThanOrEqualTo(self.view);
    }]
    
  2. 點(diǎn)擊 Button 方法:

    - (void)onGrowButtonTaped:(UIButton *)sender {
        self.scale += 1.0;
        
        //告訴self.view約束需要更新
        [self.view setNeedsUpdateConstraints];
        
        //調(diào)用此方法告訴self.view檢測(cè)是否需要更新約束,若需要?jiǎng)t更新。下面添加的動(dòng)畫(huà)效果才起作用
        [self.view updateConstraintsIfNeeded];
        
        [UIView animateWithDuration:0.3 animations:^{
            [self.view layoutIfNeeded];
        }];
    }
    
  3. 更新約束:

    // 這是Apple推薦的添加/更新約束的地方
    // 此方法可以被多次調(diào)用,以響應(yīng) setNeedsUpdateConstraints
    // 如果您需要觸發(fā)對(duì)約束的更新,可以在UIKit內(nèi)部或你的代碼中調(diào)用該方法
    - (void)updateViewConstraints {
        [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
            //這里寫(xiě)需要更新的約束,不用更新的約束將繼續(xù)存在,并不會(huì)被取代
            make.width.and.height.mas_equalTo(100*self.scale);
        }];
         // //根據(jù)Apple要求,super方法應(yīng)該在方法結(jié)束時(shí)調(diào)用
        [super updateViewConstraints];
    }
    

    ?

代碼執(zhí)行順序:

-[ViewController viewDidLoad]

-[ViewController createGrowingButton]

mas_makeConstraints

-[ViewController updateViewConstraints]

mas_updateConstraints

點(diǎn)擊按鈕

-[ViewController onGrowButtonTaped:]

-[ViewController updateViewConstraints]

mas_updateConstraints

點(diǎn)擊按鈕

-[ViewController onGrowButtonTaped:]

-[ViewController updateViewConstraints]

mas_updateConstraints

更新約束和布局的相關(guān)方法
  • 關(guān)于 UIView 重新布局相關(guān)的 API,主要有以下三個(gè):
// 標(biāo)記為需要在下一個(gè)周期重新布局
- (void)setNeedsLayout;

// 查看當(dāng)前視圖是否被標(biāo)記需要重新布局,有則在內(nèi)部調(diào)用layoutSubviews方法立即進(jìn)行重新布局
- (void)layoutIfNeeded;

// 重寫(xiě)當(dāng)前方法,在內(nèi)部完成重新布局操作,不要直接調(diào)用,如果需要強(qiáng)制更新布局,調(diào)用 setNeedsLayout()
- (void)layoutSubviews;
  • 關(guān)于更新約束布局相關(guān)的 API,主要有以下四個(gè):
// 標(biāo)記需要進(jìn)行重新布局,系統(tǒng)會(huì)調(diào)用 updateConstraints()方法,修改多個(gè)約束后調(diào)用該方法批量更新有助于提升性能
- (void)setNeedsUpdateConstraints;

// 當(dāng)前是否需要重新布局,內(nèi)部會(huì)判斷當(dāng)前有沒(méi)有被標(biāo)記的約束
- (BOOL)needsUpdateConstraints;

// 調(diào)用此方法,如果有標(biāo)記為需要重新布局的約束,則立即進(jìn)行重新布局,內(nèi)部會(huì)調(diào)用  updateConstraints方法
- (void)updateConstraintsIfNeeded;

// 重寫(xiě)此方法,內(nèi)部實(shí)現(xiàn)自定義布局過(guò)程
- (void)updateConstraints;

9.Masonry的重寫(xiě)約束:mas_remakeConstraints

mas_updateConstraints is useful for updating a set of constraints, but doing anything beyond updating constant values can get exhausting. That's where mas_remakeConstraints comes in.

mas_remakeConstraints is similar to mas_updateConstraints, but instead of updating constant values, it will remove all of its constraints before installing them again. This lets you provide different constraints without having to keep around references to ones which you want to remove.

  1. 創(chuàng)建一個(gè)Button屬性、isExpanded屬性用于記錄按鈕狀態(tài):
@property (nonatomic,strong) UIButton *growingButton;
@property (nonatomic,assign) BOOL isExpanded;
  1. 創(chuàng)建Button實(shí)例:
self.isExpanded = NO;
self.growingButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.growingButton setTitle:@"點(diǎn)我展開(kāi)" 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:_growingButton];

[self.growingButton mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.and.left.mas_equalTo(100);
    make.bottom.mas_equalTo(-350);
    make.right.mas_equalTo(-100);
}];
  1. 點(diǎn)擊Button方法:
- (void)onGrowButtonTaped:(UIButton *)sender {
    self.isExpanded = !self.isExpanded;
    if (!self.isExpanded) {
        [self.growingButton setTitle:@"點(diǎn)我展開(kāi)" forState:UIControlStateNormal];
    }else {
        [self.growingButton setTitle:@"點(diǎn)我收起" forState:UIControlStateNormal];
    }
    
    [self.view setNeedsUpdateConstraints];
    [self.view updateConstraintsIfNeeded];
    [UIView animateWithDuration:0.3 animations:^{
        [self.view layoutIfNeeded];
    }];
}
  1. 更新約束:
- (void)updateViewConstraints {
    NSLog(@"%s",__func__);
    // 這里使用update也能實(shí)現(xiàn)效果,而remark會(huì)將之前的約束全部移除,然后重新添加
    __weak typeof (self)weakself = self;
    [self.growingButton mas_remakeConstraints:^(MASConstraintMaker *make) {
        // 重寫(xiě)全部約束
        if (weakself.isExpanded) {
            make.top.and.left.mas_equalTo(0);
            make.bottom.mas_equalTo(10);
            make.right.mas_equalTo(0);
        }else {
            make.top.and.left.mas_equalTo(100);
            make.bottom.mas_equalTo(-350);
            make.right.mas_equalTo(-100);
        }
    }];
    [super updateViewConstraints];
}

實(shí)現(xiàn)結(jié)果:

其他

By default, macros which support autoboxing are prefixed with mas_. Unprefixed versions are available by defining MAS_SHORTHAND_GLOBALS before importing Masonry.

? ——摘自Masonary:README.md

  • 為了增加代碼的可讀性這里有兩個(gè)簡(jiǎn)化代碼的宏:#define MAS_SHORTHAND#define MAS_SHORTHAND_GLOBALS
  • MAS_SHORTHAND:只要在導(dǎo)入Masonry主頭文件之前定義這個(gè)宏, 那么以后在使用Masonry框架中的屬性和方法的時(shí)候, 就可以省略mas_前綴

  • MAS_SHORTHAND_GLOBALS:只要在導(dǎo)入Masonry主頭文件之前定義這個(gè)宏,那么就可以讓equalTo函數(shù)接收基本數(shù)據(jù)類(lèi)型, 內(nèi)部會(huì)對(duì)基本數(shù)據(jù)類(lèi)型進(jìn)行包裝

  • 注意:這兩個(gè)宏如果想有效使用,必須要在添加Masonry頭文件之前導(dǎo)入進(jìn)去。在沒(méi)有增加宏MAS_SHORTHAND_GLOBALS時(shí),下面這句是會(huì)報(bào)錯(cuò)的。

  • make.top.equalTo(42); --> make.top.equalTo([NSNumber numberWithInt:42]);

    ? ——摘自Masonry使用總結(jié)

注意:有時(shí)候?qū)懠s束語(yǔ)法的時(shí)候 with 經(jīng)常會(huì)不小心寫(xiě)成 width,就像這樣:

make.bottom.mas_equalTo(self.view.mas_bottom).width.offset(-80);

于是會(huì)報(bào)這樣的錯(cuò)誤:

Attributes should be chained before defining the constraint relation.

屬性應(yīng)該在定義約束關(guān)系之前鏈接.

記得仔細(xì)查找這種錯(cuò)誤并改正。

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,038評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,853評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,616評(píng)論 6 408
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,112評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,355評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,727評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,928評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,165評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,570評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,813評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,585評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,892評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容