前言
简书地址:http://www.jianshu.com/p/cc945cc667b4
本文的中文注释代码demo更新在我的github上。
AutoLayout是Apple在iOS6中新增的UI布局适配的方法,用来替代iOS6之前的AutoResizeing。AutoLayout对应的代码约束就是NSLayoutConstraint。NSLayoutConstraint的API虽说时分简单,但约束的代码量较大,所以出现了很多对NSLayoutConstraint的封装,今天讲的就是其中最为有名的Masonry框架。
Masonry框架简化了约束NSLayoutConstraint的写法,在各种APP中都有很多的使用。Masonry是基于Objective-C语言的框架,Swift项目可以参考Snapkit框架。
本文会基于Masonry v1.0.1版本,同时借鉴了网上很多解析文章,对源代码进行解析,进行学习。
16.9.14更新:
MASViewConstraint的equalToWithRelation
添加array缺少//viewConstraint.layoutRelation = relation;
的pull request已经被接受,该问题fixed。
约束
NSLayoutConstraint约束是基于以下公式:
item1.attribute1 = multiplier × item2.attribute2 + constant
比如button1的左侧距离button2有10的约束会这么写:
button1.left = button2.right + 10;
事实上添加NSLayoutConstraint约束有三种方式:
* 1.storyboard/xib添加
* 2.VFL语言添加
* 3.NSLayoutConstraint纯代码添加
1.storyboard/xib添加
storyboard/xib添加NSLayoutConstraint的方式主要是右下角的约束设置:
分别是:
* Align
* Pin
* Resolve Auto Layout Issues
相应的图这里就不再贴了,有兴趣的可以自己去试一下
2.VFL语言添加
VFL(Visual Format Language)是苹果公司为了简化autolayout的编码而推出的抽象语言。
VFL调用以下方法:
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(nullable NSDictionary<NSString *,id> *)metrics
views:(NSDictionary<NSString *, id> *)views;
其中的format
就是vfl语句。
vfl的语句也较为复杂,这里不详细介绍了,具体可以参考苹果文档Visual Format Language。
3.NSLayoutConstraint纯代码添加
我们举一个例子:
view的上部距离距离superview有10的距离:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view //view
attribute:NSLayoutAttributeTop //view.top
relatedBy:NSLayoutRelationEqual //view.top =
toItem:superView //view.top = superView
attribute:NSLayoutAttributeTop //view.top = superView.top
multiplier:1.0 //view.top = 1.0 * superView.top
constant:10.0]; //view.top = 1.0 * superView.top + 10
[view addConstraint:constraint];
可以看到约束代码算是列出了一个约束公式,也达到了约束的目的。
但这些代码,其实只写了一个top的约束,如果有其它约束代码,需要同样写类似的代码出来,所以直接用NSLayoutConstraint纯代码添加还是比较麻烦的一件事。
4.约束的限制
(1)对于两个同层级 view 之间的约束关系,添加到它们的父 view 上
(2)对于两个不同层级 view 之间的约束关系,添加到他们最近的共同父 view 上
(3)对于有层次关系的两个 view 之间的约束关系,添加到层次较高的父 view 上
(4)对于比如长宽之类的,只作用在该 view 自己身上的话,添加到该 view 自己上
Masonry的使用
1.Masonry的例子
Masonry的代码封装了NSLayoutConstraint纯代码,简洁了许多,和NSLayoutConstraint纯代码举同一个例子:
view的上部距离距离superview有10的距离
[view mas_makeConstraints:^(MASConstraintMaker *make){
make.top.equalTo(10); //默认是父view 三者等价 view.top = 1.0 * superView.top + 10
make.top.mas_equalTo(superView.mas_top).with.multipliedBy(1.0).mas_offset(10);
make.top.equalTo(superView.top).offset(10);
}];
相对于NSLayoutConstraint纯代码的添加约束,Masonry使用了block外加链式语法,使得调用简洁和方便了许多。
Masonry源代码
1.整体结构
Masonry的目录结构如下:
文件比较多,借用iOS开发之Masonry框架源码深度解析的类图:
根据类图,文件目录主要分为以下几类:
1.头文件和辅助文件
Masonry.h:头文件
MASUtilities.h:定义宏MASBoxValue,转换类型
UIView+MASShorthandAdditions.h:定义UIView调用的简化参数和方法
NSArray+MASShorthandAdditions.h:定义NSArray调用的简化参数和方法
NSLayoutConstraint+MASDebugAdditions.h/m:DEBUG相关信息转换2.约束使用接口
UIView+MASAdditions.h/m:UIView的约束接口
UIViewController+MASAdditions.h/m:UIViewController的约束借口
NSArray+MASAdditions.h/m:遍历NSArray中的UIView的约束接口3.约束建造者builder模式
MASConstraintMaker.h/m:约束构造使用的建造者builder4.约束内部结构和实现
MASLayoutConstraint.h/m:NSLayoutConstraint多了一层key的封装
MASAttribute.h:NSLayoutAttribute的一层封装
MASConstraint.h/m:定义链式结构体的抽象父类
MASConstraint+Private.h:定义MASConstraint的接口和delegate代理
MASViewConstraint.h/m:继承MASConstraint,表示view的约束结构体
MASCompositeConstraint.h/m:继承MASConstraint,表示系列view组合的约束结构体
看了源代码会发现,Masonry的代码流程简单来讲就是:提供给用户一个建造者MASConstraintMaker,让用户根据mansory提供的语法,添加约束结构体MASConstraint。最后Masonry解析约束结构体MASConstraint,将真正的约束关系NSLayoutConstraint添加到相应的view上。
2.源代码探究
我们根据一个实际调用代码来讲Masonry的源代码。代码如下:
[view mas_makeConstraints:^(MASConstraintMaker *make){
make.top.mas_equalTo(superView.mas_top).with.mas_offset(10);
}];
这边其实主要分两块
* MASConstraintMaker的链式调用
* view的约束函数调用
在讲这两块前,首先讲一下底层的数据结构MASConstraint相关的结构
(1)MASConstraint相关结构
MASConstraint是定义约束的抽象类(虽然OC没有抽象类的说法)
定义大概如下:
@interface MASConstraint : NSObject
...
- (MASConstraint * (^)(CGFloat offset))offset;
...
- (MASConstraint *)left;
- (MASConstraint *)top;
...
- (void)install;
- (void)uninstall;
这边只选取了部分方法和参数,来看一下相应的实现
- (MASC