免费开源的iOS开发学习平台

Auto Layout : 2-Masonry

虽然通过Storyboard可以非常直观的设置控件之间的约束关系,但是当界面比较复杂的情况下,通过Storyboard来设置约束会非常的难以维护,特别是涉及到团队开发时,Storyboard绝对不是优选方法。虽然苹果官方也为我们提供了通过代码编写自动布局的类--NSLayoutConstraints类,但是NSLayoutConstraints使用起来也非常的不便。鉴于此,在实际的开发中,我们经常使用第三方SDK--Masonry来替代NSLayoutConstraints。

Masonry简介

Masonry用漂亮的语法对AutoLayout进行了包装,是一个轻量级的布局框架。Masonry提供了一种链式描述约束的方法,这使得布局的代码更简洁和易读。并且Masonry支持iOS和Mac OS X。

Masonry的安装

Masonry推荐使用CocoaPods进行安装。在安装时,需要在工程的Podfile中添加下方的代码,然后更新下载即可。

pod 'Masonry'

在需要使用Masonry的文件中引入头文件:

#import "Masonry.h"

Masonry中的常用方法

Masonry是对自动布局Auto Layout的封装,因此在其中提供了与自动布局相对应的一些针对约束的操作方法,主要包括如下几个。

  • 添加约束。mas_makeConstraints:方法可以用于为所有UIView类添加约束。
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
// 创建一个MASConstraint属性
@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];
  • 更新约束。mas_updateConstraints:方法是用来更新约束的,如果约束存在则更新,如果不存在则添加新的约束。需要额外注意的是,当需要更新视图的约束时,苹果官方推荐在updateViewConstraints方法中进行更新。
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
- (void)updateViewConstraints {
    [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 updateViewConstraints];
}
  • 重新添加约束。mas_remakeConstraints:方法是用来重新添加约束的,在添加新约束前会先删除所有约束。
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
- (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);
        }
    }];
}
  • 删除约束。uninstall用于清除之前添加的所有约束。
- (void)uninstall;

Masonry中定义的关系

Masonry中有三种关系。

  1. .equalTo等价于NSLayoutRelationEqual
  2. .lessThanOrEqualTo等价于NSLayoutRelationLessThanOrEqual
  3. .greaterThanOrEqualTo等价于NSLayoutRelationGreaterThanOrEqual

这三个方法都接受下面任何一个参数。

  • 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 |

make.centerX.lessThanOrEqualTo(view2.mas_left);
  • UIView。视图的左边大于等于label的左边,下面两种写法是等价的。
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
  • NSNumber。也可以针对宽度和高度设置具体的约束大小。
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)

如果设置left、right、centerY等为一个具体值时意味着它相对于父视图left、right、centerY的位移。比如下面的代码是视图相对于父视图左边位移10个点。

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

如果你不想用NSNumer作为参数你可以使用这个以mas_equal...开头的方法来进行设置。

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));
  • NSArray。equalTo方法也接受一组上面类型的数组。
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);

示例代码

我们把上一节在Storyboard中实现自动布局的例子,使用Masonry重写一遍。首先在控制器中添加三个视图属性,并在其getter方法中赋予相对应的颜色。

#import "ViewController.h"
#import <Masonry/Masonry.h>

@interface ViewController ()
@property (nonatomic, strong) UIView *yellowView;
@property (nonatomic, strong) UIView *greenView;
@property (nonatomic, strong) UIView *redView;
@end
#pragma mark - Getter
- (UIView *)yellowView {
    if (nil == _yellowView) {
        _yellowView = [[UIView alloc] init];
        _yellowView.backgroundColor = [UIColor yellowColor];
    }
    return _yellowView;
}

- (UIView *)greenView {
    if (nil == _greenView) {
        _greenView = [[UIView alloc] init];
        _greenView.backgroundColor = [UIColor greenColor];
    }
    return _greenView;
}

- (UIView *)redView {
    if (nil == _redView) {
        _redView = [[UIView alloc] init];
        _redView.backgroundColor = [UIColor redColor];
    }
    return _redView;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.view addSubview:self.yellowView];
    [self.view addSubview:self.greenView];
    [self.view addSubview:self.redView];
}

通知UIKit需要使用AutoLayout进行布局。

+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

在updateViewConstraints方法中使用Masonry来添加和更新约束。

- (void)updateViewConstraints {
    [self.yellowView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.and.leading.equalTo(@20);
        make.width.equalTo(self.greenView);
        make.trailing.equalTo(self.greenView.mas_leading).offset(-20);
    }];
    
    [self.greenView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(@20);
        make.trailing.equalTo(@(-20));
        make.width.equalTo(self.yellowView);
        make.leading.equalTo(self.yellowView.mas_trailing).offset(20);
    }];
    
    [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.leading.equalTo(@20);
        make.bottom.and.trailing.equalTo(@(-20));
        make.top.equalTo(@[self.yellowView.mas_bottom,self.greenView.mas_bottom]).offset(20);
        make.height.equalTo(@[self.yellowView, self.greenView]);
    }];
    
    [super updateViewConstraints];
}

运行效果如下。

![屏幕快照 2016-12-20 上午10.09.27](http://oifaewd5m.bkt.clouddn.com/屏幕快照 2016-12-20 上午10.09.27.png)

![屏幕快照 2016-12-20 上午10.09.38](http://oifaewd5m.bkt.clouddn.com/屏幕快照 2016-12-20 上午10.09.38.png)