活用Masonry

Masonry is still actively maintained, we are committed to fixing bugs and merging good quality PRs from the wider community. However if you're using Swift in your project, we recommend using SnapKit as it provides better type safety with a simpler API.
-- https://github.com/SnapKit/Masonry

首先来段免责声明,Masonry现在虽然任在维护中,但是官方现在的精力在其Swift版本的SnapKit

Masonry提供了一种链式(chainable)描述AutoLayout的DSL(Domain Specific Language)。何为链式呢?比如

xiaoming.goToScholl().and.playBasketball()

类似这样,用“.”将各个词语连接起来达成一项功能的时候,这样使得表达式(DSL)更接近自然语言。上面的语句字面意思是不是就是"小明去学校打篮球去了",会几个英语单词的小学生基本都能读懂。如果真如Masonry自己描述的一样,他用这样一个“chainable”的DSL是否能够消除AutoLayout的复杂和冗余呢?

1. 赶紧尝一口

Masonry支持CocoaPods安装(废话,这么Popular的项目怎么能不支持CocoaPods)。在Pod文件中加入

pod 'Masonry'

然后执行:

pod install

在代码中,只要包含“Masonry.h”头文件就可以了

#import <Masonry/Masonry.h>

来看个例子效果,在我们写例子程序的时候,经常遇到几个按钮加个日志显示的场景,假设两个按钮在同一行,让后下面一个日志显示的TextView,这种的三角布局:

@interface ViewController ()
@property (nonatomic, strong) UIView *topLeft;
@property (nonatomic, strong) UIView *topRight;
@property (nonatomic, strong) UIView *bottom;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _topLeft = [UIView new];
    [_topLeft setBackgroundColor: [UIColor yellowColor]];
    _topRight = [UIView new];
    [_topRight setBackgroundColor:[UIColor redColor]];
    _bottom = [UIView new];
    [_bottom setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:_topLeft];
    [self.view addSubview:_topRight];
    [self.view addSubview:_bottom];
    
    
    // AutoLayout with Masonry
    [_topLeft mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(_topRight.mas_width);
        make.top.equalTo(self.view.mas_top).offset(20);
        make.left.equalTo(self.view.mas_left).offset(10);
        make.right.equalTo(_topRight.mas_left).offset(-10);
        make.bottom.equalTo(_bottom.mas_top).offset(-10);
    }];

    [_topRight mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(20);
        make.right.equalTo(self.view.mas_right).offset(-10);
        make.bottom.equalTo(_bottom.mas_top).offset(-10);
    }];
    
    [_bottom mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(_topLeft.mas_height);
        make.left.equalTo(self.view.mas_left).offset(10);
        make.right.equalTo(self.view.mas_right).offset(-10);
        make.bottom.equalTo(self.view.mas_bottom).offset(-10);
    }];
    

}

运行后效果如下:

first_blood

2.使用Masonry进行布局

看上面的代码,会发现和以往的frame+center布局的不同的是,这里不在使用initWithFrame,然后多出来一个函数调用,有且只有这一个函数调用:

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

正常来说,基本情况下使用这个一个函数就够了。Masonry还提供了另外两个- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block; 描述布局DSL作为补充,三者的原理是一样的,只是不同函数用在不同的上下文,所以只要了解上面这个函数的时候,基本上就掌握了Masonry的DSL了。

** 这里有个需要注意的点,mas_makeConstraints调用中影响的view必须是以及被addSubview,也就是必须有parentView **

先来看这个函数原型,就只有一个block参数,block内容会被及时的回调,所以这个函数其实也就是为了调用这个block而准备的,而block里面通过MASConstraintMaker作为载体来执行我们上面说的“链式”DSL,进行布局动作。所以可以认为上面的:

[_topRight mas_makeConstraints:^(MASConstraintMaker *make) {
        //make.xxx.xxx().xxx()
}];

是一个模板,填充里面make打头的DSL即可,这里make就可以认为是要布局的view的代理。对应的,如果要修改一个已经被布局的view的布局策略(只更新新的策略,原有未描述的策略保留),则使用个- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;一样的去填充“make”开头的DSL即可。或者完全重新开始布局 - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block; ,先清除所有布局,然后再加上新布局条件。

了解了这个模板以后,再来看下模板里面变个内容:

make.width.equalTo(_topRight.mas_width);        

基本格式是:

    make.attr.constrains

这里以"make"开头,然后指定其的一个属性(attr),然后再试这个属性的约束(constrains)。约束可能是多级的组合,比如.equalTo(self.view.mas_top).offset(20)的两级组合,显示找到父view的top位置,再向下(Y轴)移动20点。

3.布局中的属性

上面的attr属性主要有如下几种:

名称 autolayout 对应属性 作用
left NSLayoutAttributeLeft 左边框
top NSLayoutAttributeTop 上边框
right NSLayoutAttributeRight 右边框
bottom NSLayoutAttributeBottom 下边框
leading NSLayoutAttributeLeading 正常情况下等同于left
trailing NSLayoutAttributeTrailing 正常情况下等同于right
centerX NSLayoutAttributeCenterX center的X坐标
centerY NSLayoutAttributeCenterY center的Y坐标
baseline NSLayoutAttributeBaseline 对齐基线
leftMargin NSLayoutAttributeLeftMargin 左边的Margin
rightMargin NSLayoutAttributeRightMargin 右边的Margin
topMargin NSLayoutAttributeTopMargin 顶部的Margin
bottomMargin NSLayoutAttributeBottomMargin 底部的Margin
leadingMargin NSLayoutAttributeLeadingMargin 前导(基本等于left)Margin
trailingMargin NSLayoutAttributeTrailingMargin 后尾(基本等于tail)Margin
centerXWithinMargins NSLayoutAttributeCenterXWithinMargins 中心X坐标Margin
centerYWithinMargins NSLayoutAttributeCenterYWithinMargins 中心Y坐标Margin
width NSLayoutAttributeWidth 宽度
height NSLayoutAttributeHeight 高度

注意,这里虽然说属性,但其其实不是属性,而是一个Block定义,为了和调用时候保值一直,这里就说他们是属性了。

这里的属性其实没有什么新鲜的,和IB中调storyboard里面的“Alignment Constraints”面板里面的前七项是完全一致的,只是多了两个新的属性"width"和“�height”分别表示“宽度”和高度。

另外Margin也有对应的属性导出。

4.布局中的约束

上面的属性其实也不是属性,而是一个Block,其返回值都是MASConstraint *。而每个“MASConstraint”都有一些约束动作,其也是一个Block并且返回值任是一个MASConstraint *因此就可以用"."号进行连接类似left.equalTo(self.view.mas_left).offset(10);进行链式操作了。

下面为了符合调用时表现,将Block写成不带返回值的C函数原型的形式,而影响的属性也替换成上表中表示的属性的Blcok名。

设置偏移

调用方式 参数 效果
insets(MASEdgeInsets insets) MASEdgeInsets 设置view的四边缩小大小,等于缩放效果
sizeOffset(CGSize offset) CGSize frame的size相当于参考量的偏移大小
centerOffset(CGPoint offset) CGPoint center相对于参考量的偏移大小
offset(CGFloat offset) CGFloat 所指属性相对于参考量的偏移大小

上面的MASEdgeInsets是UIEdgeInsets的typedef:

typedef struct UIEdgeInsets {
    CGFloat top, left, bottom, right;  
} UIEdgeInsets;

类似于一个CGRect。

另外计算偏移的时候是按照iPhone的屏幕坐标的,也就是往下为Y增长方向,往右为X增长方向,所以上面的例子中(-10)表示靠近右边框往左10个点或者靠近下边框网上10个点。

设置优先级

调用方式 参数 效果
priority MASLayoutPriority 其实就是float的UILayoutPriority,设置属性的优先级
priorityLow 等于priority(MASLayoutPriorityDefaultLow)
priorityMedium 等于priority(MASLayoutPriorityDefaultMedium)
priorityHigh 等于priority(MASLayoutPriorityDefaultHigh)

这里Masonry定义了一些priority的常量

typedef UILayoutPriority MASLayoutPriority;
static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;

所以后面直接定义了low、medium以及high,在一般场景,分三个等级已经够用了,不需要再每次记着具体的数值。

关系计算

调用方式 参数 效果
equalTo(id attr) CGFloat 设置属性等于某个数值
greaterThanOrEqualTo((id attr)) CGFloat 设置属性大于或等于某个数值
lessThanOrEqualTo(id attr) CGFloat 设置属性小于或等于某个数值
multipliedBy(CGFloat multiplier) CGFloat 设置属性乘以因子后的值
dividedBy(CGFloat divider) CGFloat 设置属性除以因子后的值

连词

还有两个特殊的连词:

- (MASConstraint *)with;
- (MASConstraint *)and;

实际上他们什么也没有做,只是返回自己本身:

- (MASConstraint *)with {
    return self;
}

- (MASConstraint *)and {
    return self;
}

但是放到表达式中,却可以作为连词让链式表达式更接近自然语言。

5.总结

通过上面的实例程序,和AutoLayout的官方例子一比较也可以看得出Masonry的可读性更强,在Masonry的例子工程中演示的各种示例向我们展示了其基本能
满足大部分的布局需求,所以在布局的时候,还是比较推荐用Masonry试一下,如果产品真的有特殊的要求,再行替代也不迟,只是在demo阶段,Masonry是首要选择。

另外一个需要了解的是,当布局不成功的时候,首先请先不要怀疑Masonry,Masonry只是对AutoLayout的一个轻量封装,先想想AutoLayout的使用约束,是不是
本身的描述就有悖于AutoLayout的设计。这一点可能看文档还不够,需要多去实验才能逐步掌握,希望这边文章能够为你的实践迈出第一步。

参考

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

推荐阅读更多精彩内容