Masonry详解

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不会用宏污染全局命名空间。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容