自动布局Masonry学习笔记

优雅的自动布局,优雅的链式语法

2015-10-16 | 阅读

快速上手Masonry

Masonry是一个轻量级的布局框架,拥有自定的描述语法,使用优雅的链式语法封装自动布局,简洁明了可读性高.

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;

对应的iOS自动布局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

其中left与leading,以及right与trailing的区别,由于不同人使用的习惯不同,可能会有人以左为头,也会有人以右为头,但是left和right是固定左右的.view.mas_baseline为文本基线,为UILabel等控件文本所在的高度.

自动布局有三种比较类型,不只有相等:

  • .equalTo 相当于NSLayoutRelationEqual,为相等
  • .lessThanOrEqualTo相当于 NSLayoutRelationLessThanOrEqual,为小于或者等于
  • .greaterThanOrEqualTo 相当于 NSLayoutRelationGreaterThanOrEqual,为大于或等于

则一个简单的Masonry的写法为如下:

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
	make.left.greaterThanOrEqualTo(label.mas_left);
	make.left.right.equalTo(lable);
}];

make来代表view1,进行相对布局.两个View进行比较时,如果没有声明比较view如这里的label用来比较的值,那就拿与make相同位置的值来比较,所以上面两种写法是同一个意思.还有要注意的地方就是,make后跟的属性,都是直接left,right等,而UIView后跟的属性必须是.mas_left,mas_right.Masonry一般使用一个函数:

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block;

make中不仅属性比UIView少了前缀mas_,还多个三个属性,分别为边界,尺寸和中心:

make的属性 等效的MASViewAttribute      
     
make.edges MASAttributeTop MASAttributeLeft MASAttributeRight MASAttributeBottom
make.size MASAttributeWidth MASAttributeHeight    
make.center MASAttributeCenterX MASAttributeCenterY    

对于这三个属性,有一些特殊的用法:

make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20));
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50));
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10));

.insets是与View比较边界,等于以下形式:

make.top.equalTo(sv).with.offset(5);
make.left.equalTo(sv).with.offset(10);
make.bottom.equalTo(sv).with.offset(-15);//使用offset,统一方向,所以这里是-15
make.right.equalTo(sv).with.offset(-20);// 统一方向.

.sizeOffset也就是与view的size进行的比较,这里相当于

make.width.equalTo(sv).offset(100);
make.width.equalTo(sv).offset(-50);

.centerOffset,与view的中心点进行比较,相当于以下形式:

make.centerX.equalTo(sv).offset(-5);
make.centerY.equalTo(sv).offset(10);

Masonry也可以与数字进行比较:

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, 20, 30, 40));// 对于这种写法,已知UIEdgeInsetsMake四个值是 上左右下,则这里对左只取左值传入,也就是相当于20.

还可以与数组NSArray进行比较:

make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);

数组有多个不合理的值时,总是获取最小值来使用.相当于来取最小值.

优先级: 优先级有四种方式:

  • .priority 来设定一个明确地优先级的值,是有参数的
  • .priorityHigh 没有参数,使用默认的MASLayoutPriorityDefaultHigh
  • .priorityMedium,使用默认的MASLayoutPriorityDefaultMedium
  • .priorityLow,使用默认的MASLayoutPriorityDefaultLow值.

Masonry会优先实现优先级高的设定,发生冲突时,放弃优先级低的设定.

持有并在其他时间进行自动布局的更新

有时候,你需要定义一个存在的约束来进行动画效果或者更新删除约束.Masonry中有三种方法来更新约束constraints.

  1. References:

你可以持有一个MASConstraint*类型的约束保持在本地,然后通过installuninstall方法来使用和删除该约束.如下:

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    _topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
    //这里是保存了已经设定好的约束
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];

...
//之后需要取消的时候
[_topConstraint uninstall];
  1. mas_updateConstraints

使用mas_updateConstraints可以用来更新视图的约束,而mas_makeConstraints只能用来创建约束,一般是先创建约束,然后在合适的地方使用mas_updateConstraints来更新视图约束.

- (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];
}
  1. mas_remakeConstraints

mas_updateConstraints只能用来更新约束,但是只能更新已有约束的值,不能删除已有约束.而使用mas_remakeConstraints的使用方法与mas_updateConstraints相同,但是它在更新视图之前,会删除之前所有约束,然后再创建新的约束来更新视图.

[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);
        }
    }];

在masonry中有时候日志会打印约束错误

在为父view指定约束或者frame之前,对子view进行布局约束,如约束子view的height = 10,但是父view现在的bounds是(0,0),肯定放不下10,会提示出错。解决方法,这个View如果能够抽象,则抽象出一个自定义的View,然后统一在 updateViewConstraints中进行布局,或者先给父view一个随便的frame。

在自动布局的情况下,进行布局动画:

     [self.view layoutIfNeeded]; // Ensures that all pending layout operations have been completed
     [UIView animateWithDuration:0.3 animations:^{
     		//进行新的布局
         [self.errorViewForEmail updateConstraints:^(MASConstraintMaker *make) {
             make.height.equalTo( [x boolValue] ? 50 :0);
         }];
         [self.view layoutIfNeeded];
     }];