追求Masonry

追求Masonry

 

小笨狼 关注

 1.4 2015.12.06 15:12* 字数 4204 阅读 20062评论 29喜欢 220赞赏 3

Autolayout就像一个知情达理,善解人意的好姑娘,可惜长相有点不堪入目,所以追求者寥寥无几。所幸遇到了化妆大师cloudkite,给她来了一个完美的化妆,从此丑小鸭Autolayout变成了美天鹅Masonry。前几日有幸一见,果然名不虚传,长相甜美,还善解人意。我果断放弃了Frame,开始追求Masonry

初识Masonry

初见

我们先来看看Masonry到底有多美。

我要设置一个containView,他距离superView的上下左右边距都是10。

如果我用frame,应该是这样写的:

UIEdgeInsetsedge =UIEdgeInsetsMake(10,10,10,10);CGSizesuperSize = view.superview.frame.size;CGFloatwidth = superSize.width - edge.left - edge.right;CGFloatheitht = superSize.height - edge.top - edge.bottom;view.frame =CGRectMake(edge.left, edge.top, width, heitht);

逻辑比较复杂,阅读的时候还得想半天才能想明白,这个frame到底要表达的是什么意思。而且关键的是父View的大小如果改变,还需要再次重新设置Frame。看着Frame这黄脸婆,心里一阵别扭...

我们来看看充满青春活力的小鲜肉Masonry是怎么样的:

UIEdgeInsetsedge =UIEdgeInsetsMake(10,10,10,10);[view mas_makeConstraints:^(MASConstraintMaker *make) {    make.edges.equalTo(view.superview).insets(edge);}];

使用mas_makeConstraints在block中对View添加约束。view相对父View的边距为edge。

代码简单,逻辑一目了然。而且还能跟父View一起调整。简直是perfect,初见Masonry,惊为天人

细品

cloudkite给Autolayout披上一层漂亮的外衣之后,将其称为Masonry,但Masonry的本质还是Autolayout。

Autolayout是什么呢?Autolayout就是给View添加一堆约束,让View在布局的时候通过约束计算出Frame,然后进行布局(Autolayout更多内容见Autolayout的第一次亲密接触)。make.edges.equalTo(view.superview).insets(edge);就是添加约束的过程。

对于一个约束。他实际表示的是一个不等或者相等关系

用Masonry创建一个完整的约束应该是这样的

//view1的左边距离父View左边10个点:[view1 mas_makeConstraints:^(MASConstraintMaker *make) {    make.left.equalTo(view1.superview.mas_left).multipliedBy(1).offset(10);}];

对应到上图的表达式:

Item1: make MASConstraintMaker类型,view1的承载对象,表示View1

Attribute: left 表示左边。left的make的属性。返回值为MASConstraint类型

Relationship: equalTo 表示"="。equalTo是MASConstraint的属性

Item2: view1.superview

Attribute2: mas_left 同样表示左边,mas_left是Masonry给view加的属性,为了不重名,加了mas前缀

Multiplier: multipliedBy(1) 系数为1

Constant: offset(10) 常数为10

Attribute

MASConstraintMaker

上面的表达式中,我们可以看到,make是MASConstraintMaker类型。MASConstraintMaker给我们提供了22种Attribute类型

//Basic Attribute@property(nonatomic,strong,readonly) MASConstraint *left;@property(nonatomic,strong,readonly) MASConstraint *top;@property(nonatomic,strong,readonly) MASConstraint *right;@property(nonatomic,strong,readonly) MASConstraint *bottom;@property(nonatomic,strong,readonly) MASConstraint *leading;@property(nonatomic,strong,readonly) MASConstraint *trailing;@property(nonatomic,strong,readonly) MASConstraint *width;@property(nonatomic,strong,readonly) MASConstraint *height;@property(nonatomic,strong,readonly) MASConstraint *centerX;@property(nonatomic,strong,readonly) MASConstraint *centerY;@property(nonatomic,strong,readonly) MASConstraint *baseline;//Margin Attribute@property(nonatomic,strong,readonly) MASConstraint *leftMargin;@property(nonatomic,strong,readonly) MASConstraint *rightMargin;@property(nonatomic,strong,readonly) MASConstraint *topMargin;@property(nonatomic,strong,readonly) MASConstraint *bottomMargin;@property(nonatomic,strong,readonly) MASConstraint *leadingMargin;@property(nonatomic,strong,readonly) MASConstraint *trailingMargin;@property(nonatomic,strong,readonly) MASConstraint *centerXWithinMargins;@property(nonatomic,strong,readonly) MASConstraint *centerYWithinMargins;//Convenient Attribute@property(nonatomic,strong,readonly) MASConstraint *edges;@property(nonatomic,strong,readonly) MASConstraint *size;@property(nonatomic,strong,readonly) MASConstraint *center;

Attribute总体来说分为三大类

Basic Attribute: 基本属性,支持到iOS6,一般使用得比较多

Margin Attribute: 边缘相关属性,支持到iOS8。由于版本要求比较高,一般用得比较少。Margin相关的详细内容请参考iOS8上关于UIView的Margin新增了3个APIs

Convenient Attribute: 便捷属性,为了使用方便而特意新增的属性。Autolayout本身没有对应的相关属性

Convenient Attribute实际是基本属性的组合。比如:edges表示left, right, top, bottom。

下面的两个代码实际的意义是一样的

//Convenient Attributemake.edges.insets(edge);//Basic Attributemake.left.right.top.bottom.insets(edge);

MASConstraint

前面我们看到MASConstraintMaker中所有的Attribute都是MASConstraint类型。对于多个Attribute一起写的表达式:

make.left.right.top.bottom.insets(edge);

make.left返回的已经是MASConstraint类型,也就是说right这个Attribute是MASConstraint的属性。

MASConstraint给我们提供了19种Attribute:

//Basic Attribute@property(nonatomic,strong,readonly) MASConstraint *left;@property(nonatomic,strong,readonly) MASConstraint *top;@property(nonatomic,strong,readonly) MASConstraint *right;@property(nonatomic,strong,readonly) MASConstraint *bottom;@property(nonatomic,strong,readonly) MASConstraint *leading;@property(nonatomic,strong,readonly) MASConstraint *trailing;@property(nonatomic,strong,readonly) MASConstraint *width;@property(nonatomic,strong,readonly) MASConstraint *height;@property(nonatomic,strong,readonly) MASConstraint *centerX;@property(nonatomic,strong,readonly) MASConstraint *centerY;@property(nonatomic,strong,readonly) MASConstraint *baseline;//Margin Attribute@property(nonatomic,strong,readonly) MASConstraint *leftMargin;@property(nonatomic,strong,readonly) MASConstraint *rightMargin;@property(nonatomic,strong,readonly) MASConstraint *topMargin;@property(nonatomic,strong,readonly) MASConstraint *bottomMargin;@property(nonatomic,strong,readonly) MASConstraint *leadingMargin;@property(nonatomic,strong,readonly) MASConstraint *trailingMargin;@property(nonatomic,strong,readonly) MASConstraint *centerXWithinMargins;@property(nonatomic,strong,readonly) MASConstraint *centerYWithinMargins;

细看一下,MASConstraint中的Attribute和MASConstraintMaker完全一样。只是MASConstraintMaker中多了3种Convenient Attribute。

两者Attribute的一致,大大的提升了使用的方便性。使用过程中我们不用再去区分当前属性是MASConstraint还是MASConstraintMaker类型。(事实上没研究他的类型之前,我都不知道他们分别属于2种不同类的属性)

UIView(12月7日新增)

我们可以看到在.equalTo(view1.superview.mas_left)里面,superView也有Attribute。我们来看看UIView中有哪些Attribute:

// Basic Attribute@property(nonatomic,strong,readonly) MASViewAttribute *mas_left;@property(nonatomic,strong,readonly) MASViewAttribute *mas_top;@property(nonatomic,strong,readonly) MASViewAttribute *mas_right;@property(nonatomic,strong,readonly) MASViewAttribute *mas_bottom;@property(nonatomic,strong,readonly) MASViewAttribute *mas_leading;@property(nonatomic,strong,readonly) MASViewAttribute *mas_trailing;@property(nonatomic,strong,readonly) MASViewAttribute *mas_width;@property(nonatomic,strong,readonly) MASViewAttribute *mas_height;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerX;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerY;@property(nonatomic,strong,readonly) MASViewAttribute *mas_baseline;// Margin Attribute@property(nonatomic,strong,readonly) MASViewAttribute *mas_leftMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_rightMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_topMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_bottomMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_leadingMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_trailingMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerXWithinMargins;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerYWithinMargins;

可以看出,在UIView中的Attribute和MASConstraint中的几乎一模一样,只是每一个Attribute加了一个mas_前缀。

由于UIView是系统的类,对其扩展属性和方法一般都需要添加自己的前缀,避免跟原有属性和方法冲突。不过他们的意义跟MASConstraint中的Attribute是相同的

Relationship

约束表示的是2个item之间的关系,在Autolayout中一共定义了3种关系:=, >=, <=,对应到Masonry中:

- (MASConstraint * (^)(idattr))equalTo;- (MASConstraint * (^)(idattr))greaterThanOrEqualTo;- (MASConstraint * (^)(idattr))lessThanOrEqualTo;

相等关系我们一般用的多。那么不相等关系我们什么时候用呢?

假如我有一个Label。Label的长度不能超出父View,如果label中的文字比较短,我希望是文字有多长,Label就有多长。

由于label具有IntrinsicContentSize属性。所以默认情况下,他是文字有多长,Label就有多长。(更多IntrinsicContentSize的内容参见Autolayout的第一次亲密接触)。所以我们只需要设置Label的长度小于父View即可

[label mas_makeConstraints:^(MASConstraintMaker *make) {

    make.left.offset(0);

    make.centerY.offset(0);

    make.width.lessThanOrEqualTo(label.superview);

}];

multiplier

multiplier表示Attribute前面的乘数。Masonry提供了2种添加multiplier的方法

//  Sets the NSLayoutConstraint multiplier property- (MASConstraint * (^)(CGFloatmultiplier))multipliedBy;//  Sets the NSLayoutConstraint multiplier to 1.0/dividedBy- (MASConstraint * (^)(CGFloatdivider))dividedBy;

multipliedBy: 直接设置乘数

dividedBy: 设置乘数的倒数 multiplier = 1.0/dividedBy

一般宽或者高的约束使用multiplier比较多

Constant

Masonry提供了4种设置constant的方法

//Modifies the NSLayoutConstraint constant,only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight- (MASConstraint * (^)(MASEdgeInsets insets))insets;//Modifies the NSLayoutConstraint constant,only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeWidth, NSLayoutAttributeHeight- (MASConstraint * (^)(CGSizeoffset))sizeOffset;//Modifies the NSLayoutConstraint constant, only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeCenterX, NSLayoutAttributeCenterY- (MASConstraint * (^)(CGPointoffset))centerOffset;//Modifies the NSLayoutConstraint constant- (MASConstraint * (^)(CGFloatoffset))offset;

insets: 用来设置left, right, top, bottom。接受MASEdgeInsets类型值

sizeOffset: 用来设置width, height。接受CGSize类型的值

centerOffset: 用来设置centerX, centerY。接受CGPoint类型的值

offset: 可以用来设置所有的东西。接受CGFloat类型的值

其实一般情况下,我只使用offset....

小技巧

如果等式2边的Attribute是一样的,我们可以省略等式右边的Attribute

如果是等于关系,并且右边的view是父View。连equalTo也可以省略

如果equalTo里面传的是NSValue类型,效果跟设置offset是一样的

如果offset为0,其实也是可以省略的...

下面所有代码实际效果是一样的:

// 完整的make.left.equalTo(view1.superview.mas_left).offset(0);//省略Attribute的make.left.equalTo(view1.superview).offset(0);//省略equalTo的make.left.offset(0);//使用equalTo替代offset的make.left.equalTo(@0);//终极大招,省略所有的... 可惜会有warningmake.left;

不过对于make.left,编译器会报一个警告:你用getter方法获取回来的值未使用,所以不应该使用"."语法

对于这个警告我们可以将返回值转为空消除:

(void)make.left;

不过终究又变得麻烦了,又要多写6个字母,愁人...

设置或更新约束

对于约束的设置,Masonry提供了3种方法,分别为设置约束、更新约束、重写设置约束

// 设置约束 - (NSArray*)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;// 更新约束- (NSArray*)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;// 重新设置约束- (NSArray*)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;

mas_makeConstraints: 初次设置约束使用。

mas_updateConstraints: 更新约束时使用。如果找不着这条约束,会新增,相当于mas_makeConstraints。

mas_remakeConstraints: 重新设置约束。先将view上所有约束移除,再新增约束

注意:mas_updateConstraints只能更新已有约束。如果第一次使用的是left, right设置的相对宽度。更新的时候想换成使用width。不能使用mas_updateConstraints,因为已有约束里面没有width的约束,新增width之后会跟原有left, right约束冲突。此时应该使用mas_remakeConstraints

批量设置约束

假设有View1,view2,view3三个View,我们想要他们的宽高都等于CGSizeMake(100, 50)。我们可以对他们进行批量设置:

NSValue*sizeValue = [NSValuevalueWithCGSize:CGSizeMake(100,50)];[@[view1,view2,view3] mas_makeConstraints:^(MASConstraintMaker *make) {    make.size.equalTo(sizeValue);}];

由于我们还要设置view的top,left等位置约束。那可不可以在设置位置的mas_makeConstraints里面批量设置宽高呢?实际是可以的!

//advance set[view1 mas_makeConstraints:^(MASConstraintMaker *make) {    (void)make.top.left;    make.size.equalTo(@[view2,view3,sizeValue]);}];

不过需要注意的是。设置约束的时候,view一定是已经被addSubview的(详情参考Autolayout的第一次亲密接触),否则会抛异常。所以我们一般在最后一个view上加批量约束

Priority

我们知道约束是有优先级的,Masonry给我们提供了4个设置优先级的接口:

// Sets the NSLayoutConstraint priority to a float or MASLayoutPriority- (MASConstraint * (^)(MASLayoutPriority priority))priority;//  Sets the NSLayoutConstraint priority to MASLayoutPriorityLow- (MASConstraint * (^)())priorityLow;//  Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium- (MASConstraint * (^)())priorityMedium;//  Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh- (MASConstraint * (^)())priorityHigh;

priority: 可以设置任意的优先级,接受的参数是0-1000的数字

priorityLow: 设置低优先级,优先级为250

priorityMedium: 设置中优先级,优先级为500

priorityHigh: 设置高优先级,优先级为750

需要注意的是,使用priorityLow、priorityMedium、priorityHigh的时候。不是.priorityHigh,而是.priorityHigh()

key

当约束冲突发生的时候,我们经常为找不到是哪个View冲突的而烦恼,这一堆View是个什么东西呀?

"","","",""Will attempt to recover by breaking constraint

这时候我们可以设置View的key:

self.view.mas_key =@"self.view";view1.mas_key =@"view1";

设置之后再看一下,哈哈,现在好多了。可以清晰的知道是哪个view了

"","","",""Will attempt to recover by breaking constraint

大家可能会觉得这样一个一个设置,多麻烦啊!别着急,Masonry提供了批量设置的宏MASAttachKeys

只需要一句代码即可全部设置:

MASAttachKeys(self.view,view1);

Shorthand(12月7日新增)

在写代码的时候,可能你会感觉有的东西要加mas_前缀,有的东西又不用加,代码风格不统一,而且加mas_前缀还麻烦。

前面介绍过加mas_前缀主要是在扩展系统类的时候为了避免与原有类冲突,这是Apple推荐的做法。不过目前来说,即使不加mas_前缀,也不会有什么问题。所以Masonry提供了不加mas_前缀的方法,只需要你定义几个宏即可。

MAS_SHORTHAND

定义MAS_SHORTHAND宏之后。可以使用UIView,NSArray中不带mas_前缀的makeConstraints,updateConstraints,remakeConstraints。以及UIView中不带mas_前缀的Attribute。

MAS_SHORTHAND_GLOBALS

默认的equalTo方法只接受id类型的对象。有时候我们想传入一个CGFloat, CGSize, UIEdgeInsets等。还需要将其转化成NSValue对象,比较麻烦。Masonry也考虑到了这种情况。只需要定义MAS_SHORTHAND_GLOBALS宏。就可以直接对equalTo传入基础类型。Masonry自动转化成NSValue对象

拨开Masonry的衣服

Masonry的基本使用方法介绍完了,那么我们来看看Masonry的内部到底有些什么东西?

结构

Masonry一共有十三个类,我将这13个类分为5个模块:

Help

Help模块主要是一些辅助的类。

NSLayoutConstraint+MASDebugAdditions:这个类的主要作用是重写NSLayoutConstraint的description函数。让约束发生冲突的时候,更易读。如果View或者constraint设置了Key,直接用key的值显示到description中。如果没有设置,显示View或者constraint的指针。

ViewController+MASAdditions:提供了ViewController的LayoutGuide相关属性,以便View对齐时使用

MASUtilities:定义了一些公用的宏和属性

Shorthand

对于系统原有类(NSArray,UIView)的扩展。Masonry的category方法和属性都加有mas_前缀。这也是Apple建议的做法,避免跟系统原有方法冲突。但是有时候我们可能想用的更方便,不想写mas_前缀(没办法,我就是这么懒...)

在NSArray+MASShorthandAdditions和View+MASShorthandAdditions中定义了不带mas_前缀的扩展。这些扩展根据你是否定义了MAS_SHORTHAND宏来确定是否编译。所以你只需要定义MAS_SHORTHAND宏,就可以方便的使用不带mas_前缀的方法,比如:-[view makeConstraints:]

Public

Public模块主要是对外暴露的方法。使用者使用Masonry可以直接接触到。

NSArray+MASAdditions:主要有定义和更新约束的方法,如mas_makeConstraints:

View+MASAdditions:除了定义和更新约束的一系列方法之外,还为View增加了mas_top, mas_left等Attribute属性

Core

Core模块就是Masonry的核心部分,Masonry的大部分功能都在这4个类里实现

MASConstraintMaker:约束控制器。控制更新,删除,或者新增约束

MASConstraint:约束的基类,虚类。定义了Constraint的基本属性和方法。

MASViewConstraint: 约束的主要实现类。所有对约束使用的功能均在此类中完成

MASCompositeConstraint:约束的集合类。内部有一个数组,可以保存多个MASViewConstraint。对MASCompositeConstraint调用方法实际等于对其内部的所有MASViewConstraint调用方法

Property

此模块主要封装了一些MASConstraint持有的属性。为了使用更方便,或者扩展功能

MASViewAttribute:每一个Attribute都有一个View与之对应,为了使用更方便,所以将他们通过一个类封装在一起

MASLayoutConstraint:默认的NSLayoutConstraint是没有Key这个属性的,为了Debug方便。派生一个子类,持有key属性

实现

当我们给View添加一个约束的时候到底发生了什么?

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {

    make.left.top.equalTo(view1.superview).offset(20);

}];

我们首先来看make.left.top.equalTo(view1.superview).offset(20);

一、执行"make.left"

MASConstraintMaker类中有一个属性constraints专门用来存储constraint

@property(nonatomic,strong)NSMutableArray*constraints;

当执行make.left的时候, 会将相应的MASConstraint添加到constraints数组中

- (MASConstraint *)left {return[selfaddConstraintWithLayoutAttribute:NSLayoutAttributeLeft];}- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {return[selfconstraint:niladdConstraintWithLayoutAttribute:layoutAttribute];}//核心方法- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];//调用make.left.top时走入这里将原来的ViewConstraint替换成MASCompositeConstraintif([constraint isKindOfClass:MASViewConstraint.class]) {//replace with composite constraintNSArray*children = @[constraint, newConstraint];        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];        compositeConstraint.delegate =self;        [selfconstraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];returncompositeConstraint;    }// 调用make.left的时候走入这里,将constraint加入到self.constraints中if(!constraint) {        newConstraint.delegate =self;        [self.constraints addObject:newConstraint];    }returnnewConstraint;}

对MASConstraintMaker调用Attribute的get方法,最终都会走到-constraint:addConstraintWithLayoutAttribute:中,在这个方法中,通过对应的Attribute生成MASViewConstraint。然后将MASViewConstraint加入到constraints中

二、执行".top"

make.left返回的是MASConstraint类型。所以make.left.top是对MASViewConstraint类型调用top方法。

- (MASConstraint *)top {return[selfaddConstraintWithLayoutAttribute:NSLayoutAttributeTop];}- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {NSAssert(!self.hasLayoutRelation,@"Attributes should be chained before defining the constraint relation");return[self.delegate constraint:selfaddConstraintWithLayoutAttribute:layoutAttribute];}

当执行-addConstraintWithLayoutAttribute的时候,ViewConstraint通过delegate又调回到MASConstraintMaker的-constraint:addConstraintWithLayoutAttribute:中。

在MASConstraintMaker的-constraint:addConstraintWithLayoutAttribute:里,将原来constraints中的MASViewConstraint替换成MASCompositeConstraint。MASCompositeConstraint持有top,left 2个属性。对MASCompositeConstraint做操作时候,其内部的所有属性都会执行相应的操作

三、执行".equalTo(view1.superview)"

- (MASConstraint * (^)(id))equalTo {return^id(idattribute) {returnself.equalToWithRelation(attribute,NSLayoutRelationEqual);    };}- (MASConstraint * (^)(id,NSLayoutRelation))equalToWithRelation {return^id(idattribute,NSLayoutRelationrelation) {if([attribute isKindOfClass:NSArray.class]) {NSAssert(!self.hasLayoutRelation,@"Redefinition of constraint relation");NSMutableArray*children =NSMutableArray.new;for(idattrinattribute) {                MASViewConstraint *viewConstraint = [selfcopy];                viewConstraint.secondViewAttribute = attr;                [children addObject:viewConstraint];            }            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];            compositeConstraint.delegate =self.delegate;            [self.delegate constraint:selfshouldBeReplacedWithConstraint:compositeConstraint];returncompositeConstraint;        }else{NSAssert(!self.hasLayoutRelation ||self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class],@"Redefinition of constraint relation");self.layoutRelation = relation;self.secondViewAttribute = attribute;returnself;        }    };}

当执行Relationship的方法时,都会走到-equalToWithRelation中。

在这个方法里面主要是给realationship和secondViewAttribute赋值:

如果不是数组,直接对realationship和secondViewAttribute赋值

如果是数组,如:.equalTo(@[view1.mas_left,view2.mas_left]),逻辑上肯定不能是不等关系(>=,<=),所以realationship不用赋值,使用默认值(=)。copy出多个viewConstraint,将secondViewAttribute赋值。然后用多个viewConstraint组成的compositeConstraint替换调原来的viewConstraint。

四、执行".offset(10)"

- (MASConstraint * (^)(CGFloat))offset {return^id(CGFloatoffset){self.offset = offset;returnself;    };}- (void)setOffset:(CGFloat)offset {self.layoutConstant = offset;}

offset(10)会将10传入到ViewConstraint中,用layoutConstant属性将其存起来。(offset主要影响的是约束里面的constant)

五、mas_makeConstraints

看完了make.left.top.equalTo(view1.superview).offset(20);,我们再看看mas_makeConstraints中到底做了什么?

- (NSArray*)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {self.translatesAutoresizingMaskIntoConstraints =NO;    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];    block(constraintMaker);return[constraintMaker install];}

mas_makeConstraints方法很简单,

将self.translatesAutoresizingMaskIntoConstraints至为NO。translatesAutoresizingMaskIntoConstraints表示是否将设置的Frame转化为约束。当自己设置约束的时候需要将其置为NO

创建出MASConstraintMaker对象

通过block抛出到外面设值

constraintMaker install

上面的代码我们知道,关键的地方还是在于constraintMaker install

六、constraintMaker install

- (NSArray*)install {if(self.removeExisting) {NSArray*installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];for(MASConstraint *constraintininstalledConstraints) {            [constraint uninstall];        }    }NSArray*constraints =self.constraints.copy;for(MASConstraint *constraintinconstraints) {        constraint.updateExisting =self.updateExisting;        [constraint install];    }    [self.constraints removeAllObjects];returnconstraints;}

如果需要removeExisting,就把已有的约束remove掉,当调用mas_remakeConstraints的时候会将removeExisting值置为YES

遍历constraints,调用[constraint install]

清空constraints,这里的constraintMaker只是一个零时属性,只是一个工具类,不需要存储。所以用完之后就可以将constraints清空

其实真正关键的地方在[constraint install]

七、constraint install

- (void)install {// 1. 已经installed的将不做任何操作if(self.hasBeenInstalled) {return;    }//2. 从ViewAttribute中剥离出item和attributeMAS_VIEW *firstLayoutItem =self.firstViewAttribute.item;NSLayoutAttributefirstLayoutAttribute =self.firstViewAttribute.layoutAttribute;    MAS_VIEW *secondLayoutItem =self.secondViewAttribute.item;NSLayoutAttributesecondLayoutAttribute =self.secondViewAttribute.layoutAttribute;//3. 如果没有secondViewAttribute,默认secondItem为其父View,secontAttribute等于firstLayoutAttribute。if(!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {        secondLayoutItem =self.firstViewAttribute.view.superview;        secondLayoutAttribute = firstLayoutAttribute;    }//4. 创建真正用于Autolayout的约束layoutConstraintMASLayoutConstraint *layoutConstraint        = [MASLayoutConstraint constraintWithItem:firstLayoutItem                                        attribute:firstLayoutAttribute                                        relatedBy:self.layoutRelation                                          toItem:secondLayoutItem                                        attribute:secondLayoutAttribute                                      multiplier:self.layoutMultiplier                                        constant:self.layoutConstant];//5. 将priority和key赋值layoutConstraint.priority =self.layoutPriority;    layoutConstraint.mas_key =self.mas_key;//6. 找到要添加约束的installViewif(self.secondViewAttribute.view) {        MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];NSAssert(closestCommonSuperview,@"couldn't find a common superview for %@ and %@",self.firstViewAttribute.view,self.secondViewAttribute.view);self.installedView = closestCommonSuperview;    }elseif(self.firstViewAttribute.isSizeAttribute) {self.installedView =self.firstViewAttribute.view;    }else{self.installedView =self.firstViewAttribute.view.superview;    }//7. 添加或更新约束MASLayoutConstraint *existingConstraint =nil;if(self.updateExisting) {        existingConstraint = [selflayoutConstraintSimilarTo:layoutConstraint];    }if(existingConstraint) {// just update the constantexistingConstraint.constant = layoutConstraint.constant;self.layoutConstraint = existingConstraint;    }else{        [self.installedView addConstraint:layoutConstraint];self.layoutConstraint = layoutConstraint;        [firstLayoutItem.mas_installedConstraints addObject:self];    }}

如果已经installed就不做任何操作

从ViewAttribute中剥离出item和attribute。前面我们介绍过MASViewAttribute的类主要是将item和attribute2个属性封装在了一起。

secondViewAttribute的值来自于.equalTo(item.attribute)中的item.attribute。当我们写下类似make.left.offset(10);约束的时候,是没有secondViewAttribute的,这时候默认secondItem为其父View,secontAttribute等于firstLayoutAttribute。这就解释了为什么可以这样写make.left.offset(10);

创建真正用于Autolayout的约束layoutConstraint

将priority和key赋值

找到要添加约束的installView。如果是2个View之间的约束,需要寻找这2个View最接近的共同父View。添加约束

添加或更新约束。当调用mas_updateConstraints的时候updateExisting=YES。这时候会查找是否有已经存在的约束。有就更新,没有就添加。如果是mas_makeConstraints或mas_remakeConstraints,则直接添加

Extension

仅仅将代码结构和基本实现过程解析了一下,更多实现细节还需要大家自己去阅读源码

说实话,Masonry的代码写得真漂亮,不管是代码格式规范,还是设计模式。看起来简直是一种享受。建议大家阅读。

Autolayout的第一次亲密接触也更新了一些东西。没阅读过或者阅读时间比较早的朋友可以再看看~

Reference

Masonry源码

Autolayout的第一次亲密接触

iOS8上关于UIView的Margin新增了3个APIs

About me

我的博客

我的微博

你可能感兴趣的:(追求Masonry)