Masonry详解

作者: 夏天的风_song | 来源:发表于2017-07-02 14:07 被阅读221次

    Masonry GitHub地址

    ** Masonry仍然在积极维护,但是如果您在的项目中使用Swift,我们建议使用SnapKit,因为它可以通过更简单的API,提供更好的类型安全性。**

    Masonry是一个轻量级的布局框架,它将AutoLayout与更好的语法相结合。Masonry有自己的布局DSL,它提供了一种可链接的方式来描述您的NSLayoutConstraints,从而导致更简洁和可读性的布局代码。Masonry支持iOS和Mac OS X.

    例如,请查看Masonry工作空间中的Masonry iOS Examples项目。pod install下载后需要运行。

    NSLayoutConstraints有什么问题?

    自动布局是组织和布置您的视图的强大而灵活的方式。然而从代码创建约束是冗长而不是很描述性的。想象一下,一个简单的例子,你想要有一个视图填充其超级视图,视图距离其父视图的每个边界都是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],
    
     ]];
    

    即使有这样一个简单的例子,当你有超过2或3个视图时,所需的代码是相当冗长的,并且很快变得不可读。另一个选择是使用视觉格式语言(VFL),这是一个不那么长的时间。然而,ASCII类型的语法有自己的陷阱,它也有点难以动画,因为NSLayoutConstraint constraintsWithVisualFormat:返回一个数组。

    准备好迎接你的上帝!

    使用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会自动地添加约束到适当的视图。Masonry 会为你自动调用view1.translatesAutoresizingMaskIntoConstraints = NO;

    并不是所有的东西都是平等的

    .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 / NSView

    如果你想要view.left大于或等于label.left:

    //这两个约束完全一样
    make.left.greaterThanOrEqualTo(label);
    make.left.greaterThanOrEqualTo(label.mas_left);
    

    3、NSNumber

    自动布局允许将宽度和高度设置为常量值。如果要将视图设置为具有最小和最大宽度,则可以将数字传递给相等块:

    // width> = 200 && width <= 400 
    make.width.greaterThanOrEqualTo(@ 200);
    make.width.lessThanOrEqualTo(@ 400)
    

    但是自动布局不允许将对齐属性(例如left,right,centerY等)设置为常量值。所以如果你传递这些属性的NSNumber,那么Masonry会把它们变成相对于view的superview的约束,即:

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

    您可以使用基本数据类型和结构体来构建约束,而不是使用NSNumber,如下所示:

    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));
    

    默认情况下,支持自动封装的宏是前缀mas_。MAS_SHORTHAND_GLOBALS在导入Masonry之前定义,可以使用未加上的版本。

    4、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。

    边缘

    // 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);
    

    Hold on for dear life

    有时您需要修改现有约束以便动画或删除/替换约束。在Masonry中,有几种不同的更新约束方法。

    1、References

    您可以通过将约束make表达式的结果分配给局部变量或类属性来保持特定约束的引用。您还可以通过将它们存储在数组中来引用多个约束。

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

    或者,如果您只更新约束的常量值,则可以使用convience方法mas_updateConstraints而不是mas_makeConstraints

    //这是苹果推荐添加/更新约束的地方
    //调用`setNeedsUpdateConstraints `时,这个方法可以得到多次调用
    //如果你需要触发更新了自己的约束,可以通过UIKit的内部或在你的代码使用
    - (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类似于mas_updateConstraints但不是更新常量值,它将在重新安装之前删除所有约束。这可以让您提供不同的约束,而无需保留对要删除的引用。

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

    您可以在Masonry iOS示例项目中找到更详细的三种方法的例子。

    When the ^&*!@ hits the fan!

    约束并不是都很顺利。所以当控制台有这样的输出:

    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。现在,您可以给观点和约束提供有意义的名称,并且还可以轻松地挑选出由Masonry创建的约束。

    有关如何设置此项的示例,请查看Masonry工作空间中的Masonry iOS示例项目。

    我应该在哪里创建我的约束?

    @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];
    }
    
    @end
    

    Installation

    使用

    orosome CocoaPods

    在你的Podfile中

    pod 'Masonry'

    如果你想使用没有所有那些麻烦的'mas_'前缀的masonry。在导入masonry之前,将#define MAS_SHORTHAND添加到您的prefix.pch中

    define MAS_SHORTHAND

    引入头文件

    import "Masonry.h"

    代码片段

    将包含的代码片段复制为

    `~/Library/Developer/Xcode/UserData/CodeSnippets`
    以闪电般的速度编写您的Masonry!

    mas_make -> [<view> mas_makeConstraints:^(MASConstraintMaker *make){<code>}];

    mas_update-> [<view> mas_updateConstraints:^(MASConstraintMaker *make){<code>}];

    mas_remake -> [<view> mas_remakeConstraints:^(MASConstraintMaker *make){<code>}];

    特征

    • 不限于自动布局的子集。
      任何NSLayoutConstraint都可以做,Masonry也可以做!

    • 很好的调试支持,给你的意见和约束有意义的名字。

    • 约束看起来像句子。

    • 没有疯狂的宏观魔法。
      Maonry不会用宏污染全局命名空间。

    • 不是字符串或字典,因此您可以编译时间检查。

    相关文章

      网友评论

      本文标题:Masonry详解

      本文链接:https://www.haomeiwen.com/subject/pbyncxtx.html