Masonry
Masonry是一个轻量级的布局框架,它使用更好的语法包装AutoLayout。 Masonry有自己的布局DSL,它提供了一种描述NSLayoutConstraints的可链接方式,从而使布局代码更简洁,更易读。 Masonry支持iOS和Mac OS X。
AutoLayout
先看一下,如果使用原生的 AutoLayout,需要那些代码
子视图离父视图上下左右各有10个像素的间距
1 | UIView *superview = self.view; |
但如果使用 Masonry
的话,代码量以及阅读起来都十分可观
1 | UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); |
是不是非常简洁,调用起来方便,阅读起来也清晰明了
Masonry 会自动将约束添加到适当的视图中, 且自动调用 view1.translatesAutoresizingMaskIntoConstraints = NO;
Masonry
看一下 mas_makeConstraints 方法声明
1 | - (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; |
这个方法传递的参数是一个参数为 MASConstraintMaker
类型的无返回值的 block
,而该方法的返回值则是一个数组。
方法声明中我们看到了一个叫做 NS_NOESCAPE
的宏,NS_NOESCAPE
用于修饰方法中的 block 类型参数,作用是告诉编译器,该 block 在方法返回之前就会执行完毕,而不是被保存起来在之后的某个时候再执行。编译器被告知后,就会相应的进行一些优化。更详细的内容请参考 Add @noescape to public library API
看一下 mas_makeConstraints 方法实现
1 | - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { |
看一下 MASConstraintMaker
的 init 方法
1 | @interface MASConstraintMaker () <MASConstraintDelegate> |
看一下 block(constraintMaker); 将 constraintMaker
传递给我们,方便使用属性添加约束
1 | make.top.equalTo(superview.mas_top).with.offset(padding.top); |
看下 make.top
make 是 MASConstraintMaker 类型的对象,这个类型封装了一系列只读 MASConstraint 属性,top 就是其中之一,声明和实现如下:
1 | @property (nonatomic, strong, readonly) MASConstraint *top; |
addConstraintWithLayoutAttribute
方法的实现如下:
1 | - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { |
再来看下 equalTo
方法,也是用 MASConstraint 调用的,并且返回 MASConstraint 对象,方便链式调用1
2
3
4
5
6
7
8
9
10
11
12
13- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); }
#define MASMethodNotImplemented() \
//
@throw [NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
userInfo:nil]
而我们在 makeConstraints 的时候,实际调用的是 MASViewConstraint 这个 MASConstraint 子类中的实现:
1 | - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { |
该方法接收两个参数,一个表示了对应的属性(mas_top),一个表示了相等关系(NSLayoutRelationEqual),进入方法后会先对我们传入的属性做一个类型判断,我们传入的是一个单个的属性,所以会落入 else 分支,同样是依赖断言做了一系列保护性的判断,并将相等关系和视图属性分别赋值给 layoutRelation 和 secondViewAttribute 属性,并返回 self。
返回 self,看似简简单单的一个操作,却是 Masonry 能够实现链式 DSL 最重要的基石。
superview.mas_top
再来看看传入的 mas_top,这是一个声明在 View+MASAdditions.h 当中的只读属性:
1 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_top; |
.with
1 | - (MASConstraint *)with { |
.offset
1 | - (MASConstraint * (^)(CGFloat offset))offset; |
[constraintMaker install];
在配置好想要的约束后,调用 [constraintMaker install];
对视图施加约束
1 | - (NSArray *)install { |
对 constraints 属性做一份 copy后,遍历 constraints 中的所有 MASConstraint 及其子类型的属性,并调用其 install 方法:
1 | - (void)install { |