MasonryGitHub上的官方使用教程翻译

Masonry仍旧在持续维护中,借助于开放社区,我们致力于修复漏洞和合并优秀的需求。然而你的项目是用Swift语言的,我们推荐使用SnapKit,因为它有着更简短的API来更好适应Swift.
Masonry是一种轻量级的布局框架,采用了更良好的语言来封装AutoLayout。Masonry有自己的布局DSL,提供了一种链式方式来描述你的NSLayoutConstraints,通过这种方式产生的布局代码更简洁,更易读。Masonry支持iOS和Mac OS X.

NSLayoutConstraints的弊端在哪?
通过底层的AutoLayout可以比较有效和灵活的组织和布局你的Views,然而通过代码创建约束比较冗长且不生动。试想这样一个例子:创建一个View距离父视图的边界都为10,代码如下:

UIView *superview = self.view;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[

    //view1 constraints
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeLeft
                                multiplier:1.0
                                  constant:padding.left],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeBottom
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeBottom
                                multiplier:1.0
                                  constant:-padding.bottom],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeRight
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeRight
                                multiplier:1
                                  constant:-padding.right],

 ]];

像这种简单的例子代码都如此冗长,当你的视图复杂的时候可读性就更差了。另外一种方式可以使用VFL,代码更少点,不过这种ASCII风格的语言有它自己的弊端并且实现动画效果比较难,就比如NSLayoutConstraint constraintsWithVisualFormat:返回一个数组。
认识下MASConstraintMaker
同样的例子我们使用MASConstraintMaker来编写

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
    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);
}];

甚至可以更短

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

当然要注意到在第一个例子中我们必须给父视图添加约束[superview addConstraints:…]不过Masonry会自动给最合适的视图添加约束的,并且也默认设置了view1.translatesAutoresizingMaskIntoConstraints = NO

不是所有的约束都是equal
.equalTo 相当于 NSLayoutRelationEqual
.lessThanOrEqualTo 相当于 NSLayoutRelationLessThanOrEqual
.greaterThanOrEqualTo 相当于 NSLayoutRelationGreaterThanOrEqual

这三个等于约束可以接收以下任意一种作为它的参数:

1.MASViewAttribute
make.centerX.lessThanOrEqualTo(view2.mas_left);

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.UIView
两者效果等价

make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
  1. NSNumber
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)

AutoLayout 可以允许宽度和高度设置为常量,但是像left、right、CenterX这些属性是不允许赋予常量的,当你赋予一个常量的时候相当于和它的父视图做对比,比如:

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

当然,如果你不想对数字进行装箱,可以使用前缀mas_,这个会自动帮你装箱,如果你不想加这个前缀,可以在导入Masonry前定义一个宏MAS_SHORTHAND_GLOBALS。

  1. NSArray
    包含各种类型的数组
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);

学会使用优先级

.priority 允许你使用指定的优先级

.priorityHigh 相当于 UILayoutPriorityDefaultHigh

.priorityMedium 介于高优先级和低优先级之间

.priorityLow 相当于 UILayoutPriorityDefaultLow

优先级一般放在链式语法的最后,就像这样

make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();

make.top.equalTo(label.mas_top).with.priority(600);

组合,组合,组合
Masonry提供了一些便捷的方式可以在同一时间创建多个约束,这些称为MASCompositeConstraints

edges(边距)
// make top, left, bottom, right equal view2
make.edges.equalTo(view2);

// make top = superview.top + 5, left = superview.left + 10,
//      bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))

size(尺寸)
// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)

// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))

center(中心)
// make centerX and centerY = button1
make.center.equalTo(button1)

// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))

你也可以使用链式来增加可读性
// All edges but the top should equal those of the superview
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);

增加更好的体验
有时候你需要修改约束为了达到动画效果或者移除/更新约束。Masonry提供了多种不同方法更新约束

1. References
你可以使用属性或者成员变量来持有一个特定的约束,或者使用数组来持有多个约束
// in public/private interface
@property (nonatomic, strong) MASConstraint *topConstraint;

...

// when making constraints
[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);
}];

...
// then later you can call
[self.topConstraint uninstall];

2. mas_updateConstraints (更新约束)
如果你只是想更新约束的常量值,你可以使用mas_updateConstraints来代替mas_makeConstraints
// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
- (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);
    }];

    //according to apple super should be called at end of method
    [super updateConstraints];
}

3. mas_remakeConstraints
mas_updateConstraints用于更新一系列约束比较有用,但是对于不是更新约束常量值的时候显然没用,这就需要mas_remakeConstraints,它会删除掉之前所有创建的约束,这个会产生一些新的约束,这样你可以不用持有那些你需要删除的约束。
- (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);
        }
    }];
}

你可以在官方Demo中看到更多的关于这三种方式的使用。

使用中可能遇到的问题
布局视图并不一定都会出现预期效果,当发现错误崩溃的时候,你肯定不想看到控制台输出以下语句:

Unable to simultaneously satisfy constraints.....blah blah blah....
(
    "<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>",
    "<NSAutoresizingMaskLayoutConstraint:0x839ea20 h=--& v=--& V:[MASExampleDebuggingView:0x7186560(416)]>",
    "<NSLayoutConstraint:0x7189c70 UILabel:0x7186980.bottom == MASExampleDebuggingView:0x7186560.bottom - 10>",
    "<NSLayoutConstraint:0x7189560 V:|-(1)-[UILabel:0x7186980]   (Names: '|':MASExampleDebuggingView:0x7186560 )>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>

Masonry对NSLayoutConstraint加了一个分类,重写了它的- (NSString *)description,你可以给视图和约束起一个有意义的名称,而且可以很容易找出那些约束,所以错误时你的控制台就会输出这样清晰报错信息:

Unable to simultaneously satisfy constraints......blah blah blah....
(
    "<NSAutoresizingMaskLayoutConstraint:0x8887740 MASExampleDebuggingView:superview.height == 416>",
    "<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>",
    "<MASLayoutConstraint:BottomConstraint UILabel:messageLabel.bottom == MASExampleDebuggingView:superview.bottom - 10>",
    "<MASLayoutConstraint:ConflictingConstraint[0] UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1>"
)

Will attempt to recover by breaking constraint
<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>

在官方Demo有专门调试的例子介绍

那么我应该在哪里创建我的约束呢
苹果的推荐做法如下:

@implementation DIYCustomView

- (id)init {
    self = [super init];
    if (!self) return nil;

    // --- Create your views here ---
    self.button = [[UIButton alloc] init];

    return self;
}

// tell UIKit that you are using AutoLayout
+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {

    // --- remake/update constraints here
    [self.button remakeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(@(self.buttonSize.width));
        make.height.equalTo(@(self.buttonSize.height));
    }];

    //according to apple super should be called at end of method
    [super updateConstraints];
}

- (void)didTapButton:(UIButton *)button {
    // --- Do your changes ie change variables that affect your layout etc ---
    self.buttonSize = CGSize(200, 200);

    // tell constraints they need updating
    [self setNeedsUpdateConstraints];
}

猜你喜欢

转载自blog.csdn.net/xj_love/article/details/53688586