Masonry这个框架是使用代码进行自动布局使用的,它的使用很是普遍,这段时间一直在学习这个框架,所以想把学到的东西记下来,方便之后查阅,也便于与人分享。数组
自动布局约束的等式:bash
Masonry中使用了大量的点链式语法,考虑到应该有些小伙伴不知道点链式语法的前因后果,所以这里先整理一下点链式语法。app
咱们先来看一下Masonry框架的一种使用:框架
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(superview.mas_left).mas_offset(30);
}];
复制代码
上面的代码是Masonry的简单的使用,这里面就用到了点链式语法make.left.equalTo(superview.mas_left).mas_offset(30);
,咱们看一下这句点链式语法,这里面包括三个要素:less
咱们能够声明一些Block类型的属性,让block类型的属性的返回值为其自己。ide
下面用一个计算器的例子来讲明一下:布局
//Calculator.h
@interface Calculator : NSObject
//这里是建立一个属性,属性的类型是block类型,属性名是add
@property (nonatomic, copy)Calculator * (^add)(NSInteger num);
@property (nonatomic, copy)Calculator * (^minus)(NSInteger num);
@property (nonatomic, copy)Calculator * (^multiply)(NSInteger num);
@property (nonatomic, copy)Calculator * (^divide)(NSInteger num);
@property (nonatomic, assign)NSInteger result;
@end
复制代码
//Calculator.m
@implementation Calculator
- (instancetype)init
{
self = [super init];
if (self) {
self.result = 0;
}
return self;
}
//这里实现的是add这个属性的get方法,只不过属性的类型是block类型的。
- (Calculator * (^)(NSInteger num))add
{
return ^id(NSInteger num){
self.result += num;
return self;
};
}
- (Calculator * (^)(NSInteger num))minus
{
return ^id(NSInteger num){
self.result -= num;
return self;
};
}
- (Calculator * (^)(NSInteger num))multiply
{
return ^id(NSInteger num){
self.result *= num;
return self;
};
}
- (Calculator * (^)(NSInteger num))divide
{
return ^id(NSInteger num){
self.result /= num;
return self;
};
}
@end
复制代码
调用:学习
Calculator *calculator = [[Calculator alloc] init];
calculator.add(5).minus(8).multiply(8).divide(23);
复制代码
return ^id(NSInteger num){
self.result += num;
return self;
};
复制代码
@interface Calculator : NSObject
/*
@property (nonatomic, copy)Calculator * (^add)(NSInteger num);
@property (nonatomic, copy)Calculator * (^minus)(NSInteger num);
@property (nonatomic, copy)Calculator * (^multiply)(NSInteger num);
@property (nonatomic, copy)Calculator * (^divide)(NSInteger num);
@property (nonatomic, assign)NSInteger result;
*/
- (Calculator * (^)(NSInteger num))add;
- (Calculator * (^)(NSInteger num))minus;
- (Calculator * (^)(NSInteger num))multiply;
- (Calculator * (^)(NSInteger num))divide;
@end
复制代码
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);
}];
复制代码
lessThanOrEqualTo:等同于NSLayoutRelationLessThanOrEqual greaterThanOrEqualTo:等同于NSLayoutRelationGreaterThanOrEqualui
Masonry中有MASViewAttribute这个类,这个类就等同于NSLayoutAttribute这个类:
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 |
自动布局不容许对齐的属性如left,right,centerY等设置为常数,若是咱们传了一个常数给这些属性,Masonry会自动把这些约束变为相对于父视图的约束,即:
//creates view.left = view.superview.left + 10
make.left.equalTo(@10)
复制代码
在使用Masonry的时候,有时候会比较迷糊何时使用带有mas前缀的,何时使用不带前缀的,咱们看下面这句代码:
make.top.mas_equalTo(42);
复制代码
这句代码也能够这样写:
make.top.equalTo(@42);
复制代码
可是这样写就会报错:
make.top.equalTo(42);
复制代码
缘由就在于这个括号里面的参数类型必须是id类型,若是括号里面的参数不传id类型就传常量类型也行,那么就必需要在equalTo前面加上mas,加上mas后,mas_equalTo会把传进来的数值类型变成id类型。
Masonry给了咱们几个便利的方法来让咱们一次性建立多个约束,Masonry中与这个约束相关的类是MASCompositeConstraint类,简单使用以下: edges
// 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))
复制代码
当咱们只是修改约束的constant的时候,可使用mas_updateConstraints
:
// this is Apple's recommended place for adding/updating constraints // this method can get called multiple times in response to setNeedsUpdateConstraints // which can be called by UIKit internally or in your code if you need to trigger an update to your constraints - (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]; } 复制代码
当咱们要修改的不止是约束的constant的时候,使用mas_updateConstraints
就力不从心了,这时就须要使用mas_remakeConstraints
:
- (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);
}
}];
}
复制代码
咱们在解读源码的时候先从最简单最基础的使用开始,而后由浅入深,逐渐深刻。下面咱们先分析一下整个框架的文件结构:
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(superview.mas_left).mas_offset(30);
}];
复制代码
咱们先无论外面的方法调用,只须要知道make是MASConstraintMaker类型的就好了,从make.left.equalTo(superview.mas_left).mas_offset(30);
开始:
make.left
left是它的一个属性,这里调用的是属性的getter方法。
那么如今咱们来总结一下make.left作了哪些事:
make.left是调用了MASConstraintMaker类的left属性的get方法,这里建立了一个MASViewAttribute对象,这个对象由一个UIView对象和一个NSLayoutAttribute来建立,这里UIView对象是view,NALayoutAttribute为NSLayoutAttributeLeft,因此这里MASViewAttribute对象也就是封装了约束等号左边的两个元素。。而后使用建立的MASViewAttribute对象来建立了一个MASViewConstraint对象,这个对象表明这一行代码所表示的整个约束。最终make.left返回了一个MASVIewConstraint对象。
须要注意的是,MASConstraintMaker
对象有一个数组类型的consrtaints
属性,新建立的MASViewConstraint对象被加入到了这个属性中,在最后添加约束的时候会遍历这个数组。
superview.mas_left
mas_left是分类的一个属性,因此superview.mas_left会调用分类的-(MASViewAttribute *)mas_left
方法。
superview.mas_left返回了一个MASViewAttribute对象,这个对象封装了约束等号右边的两个元素。
make.left.equalTo(superview.mas_left)
进入equalTo查看具体实现:
make.left.equalTo
会获得一个Block,那么我执行make.left.equalTo(superview.mas_left)
就是执行这个Block,即make.left.equalTo(superview.mas_left)
会执行self.equalToWithRelation(attribute, NSLayoutRelationEqual)
这一行核心代码,并返回这一行核心代码的返回值。 因为make.left是MASViewConstraint对象,因此咱们要去MASViewConstraint类中查看equalWithRelation的实现:
self.secondViewAttribute = attribute;
会触发secondViewAttribute
这个属性的set方法,咱们看一下其set方法的实现:
make.left.equalTo()
这个括号里面传入的东西可能有三种状况,第一种是数字,第二种是一个UIView对象,若是是UIView对象,那就将其layoutAttribute设置为什么firstAttribute一致,也就是咱们也能够这样写:make.left.equalTo(superview)
,这样Masonry也能成功识别。第三种是传入的MASViewConstraint对象,若是传入这种对象则能够直接赋值给secondViewAttribute属性。 下面咱们再来看一下第一种状况传入数字的处理方式,咱们进入setLayoutConstantWithValue:
这个方法:
这样咱们就清楚了
make.left.equalTo()
这个括号中传入各类不一样类型的值会怎么操做。
总结一下make.left.equalTo(superview.mas_left)
作的事情:
make.left
建立了一个firstViewAttribute
,firstViewAttribute
的view属性即为mas_makeConstraint
方法的调用者,其layoutAttribute
属性为NSLayoutAttributeLeft
,firstViewAttribute
封装了约束等式左边的两个item。接着经过传入firstViewAttribute
建立了一个MASViewConstraint
对象。superview.mas_left
则是建立了一个secondViewAttribute
对象,该对象的view即为superview,layoutAttribute
为NSLayoutAttributeLeft
。make.left.equalTo(supervie.mas_left)
则是将secondViewAttribute
赋值给MASViewConstraint
对象的secondViewAttribute
属性,并给MASViewConstraint
对象的layoutRelation
属性赋值。
.mas_offset(30)
mas_offset
的颜色是土黄色,说明这是一个宏定义,咱们点进去,发现这个宏定义是定义在MASConstraint.h
文件中:#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__)))
复制代码
这不是一个简单的宏定义,里面还进行了嵌套,咱们看一下MASBoxValue()
方法作了什么,在MASUtilities.h
这个文件中找到了MASBoxValue()
这个方法:
在Masonry的用法这部分我说过mas前缀的使用,这里咱们就看到了其实现方法。若是咱们括号里想要直接传入数值类型而不是id类型的参数,那么前面使用的API就必须带mas前缀,不然报错,若是传入的是id类型,则前面使用的API是否带mas前缀都可。
因此.mas_offset(30)
也就等于.valueOffset(@30)
,那么咱们来查看一下MASConstraint.m
中的实现:
MASViewConstraint
的layoutConstant属性:
make.left.equalTo(superview.mas_left).mas_offset(30);
这行代码就全解读完了。总结一下就是:
make,left
建立了一个MASViewConstraint
对象,为这个对象的firstViewAttribute
属性赋值,superview.mas_left
即建立了一个MASViewAttribute
对象,equalTo()
即把这个MASViewAttribute
对象赋值给MASViewConstraint
对象的secondViewAttribute
属性,.mas_offset(30)
则是给MASViewConstraint
对象的layoutConstant
属性赋值为30.
mas_makeConstraints:
mas_makeConstraints:
这个方法是在UIView的分类中定义并实现的,下面咱们看一下其具体实现:
[constraint install]:
,这个方法的内容比较多,咱们分两部分来看:
首先根据
MASViewConstraint
对象的firstViewAttribute
和secondViewAttribute
这两个属性,访问这两个属性获得firstLayoutItem
,firstLayoutAttribute
。secondLayoutitem
,secondLayoutAttribute
。而后处理有时是对齐属性如left没有提供view的状况,这时就要设置view为其父视图。而后寻找应该将约束添加到哪一个视图上,最后添加约束到对应的视图上。
Masonry中能够直接约束size,center,edge这样的组合约束。其本质也就是把它拆成多个单个约束,好比对size的约束,就是拆成width和height这两个约束。下面咱们看一下其具体的实现方法:
make.size.equalTo(superview).sizeOffset(CGSizeMake(10, 10));
复制代码
make.size
- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs
方法:
make.size
作了哪些事:把size这一个拆分红了width和height这两个,根据这两个约束建立了两个
MASViewConstraint
对象,装到一个数组里面,使用这个数组来建立了一个MASCompositeConstraint
对象,最后返回这个MASCompositeConstraint
对象。
.equalTo(superview)
make.size.equalTo(superview)
:把size这一个约束拆分红了width和height这两个约束,并根据此建立了两个
MASViewConstraint
对象,根据这两个对象组成的数组去建立一个MASCompositeConstraint
对象。而后遍历MASCompositeConstraint
对象的childConstraints
数组,取出数组里面的额每一个MASViewConstraint
对象,而后像处理单个MASViewConstraint
同样去处理。
.sizeOffset(CGSizeMake(10, 10))
能够想象就是把它拆分,而后赋值给两个MASViewConstraint的layoutConstant属性,就不详细说了。自动布局约束等式: item1.attribute1 = multiplier × item2.attribute2 + constant
有时候咱们有更改约束的需求,好比咱们要作一个动画,移动某个视图,那就须要改变视图约束,当咱们只是改变约束的constant时,可使用
mas_updateConstraints:
这个方法。而当咱们须要改变的不止constant时,就须要调用mas_remakeConstraints:
这个方法了。
先来看一下mas_updateConstraints:
这个方法是怎么实现只改变约束的constant的:
[constraintMaker install]
[constraint install]
中是怎么实现的:
总结起来,
mas_updateConstraints:
就是去self.installedView.constarints
这个属性数组中去遍历,看有没有这样一个NSLayoutConstraint
对象,它除了constant外,全部内容都和当前的NSLayoutConstraint
对象一致,若是有这个对象,那么就把该对象的constant改成当前NSLayoutConstraint
对象的constant。这样来完成约束条件的更新。
再来看一下mas_remakeConstraints:
当咱们要改变的约束条件不止是constant这么简单时,使用mas_remakeConstraints:
就不顶用了,就要使用mas_remakeConstraints:
这个方法。
[constraintMaker install]
:
这篇文章在简书的地址:www.jianshu.com/p/8990c5a98…