iOS开发经过代码方式使用 NSLayoutConstraint + Masonry

随着iPhone6/6+设备的上市,如何让手头上的APP适配多种机型多种屏幕尺寸变得尤其迫切和必要。(包括:iPhone4/4s,iPhone5/5s,iPhone6/6s,iPhone 6p/6ps)。html

在 iPhone6出现之前,咱们接触的iPhone屏幕只有两种尺寸:320 x 480和320 x 568。因此在那个时候使用传统的绝对定位(Frame)方式进行界面控件的布局仍是比较轻松的,由于咱们只须要稍微调整一下Frame就能够适配这两种 大小的屏幕了。也许这也是为何虽然AutoLayout从IOS6就已经出现了,可是对于AutoLayout的使用和普及好像都不怎么火热。不过直到 最近随着iPhone6/6+设备的出现,AutoLayout又被众多开发者从新审视和重视了。毕竟APPLE推出AutoLayout就是为了帮助开 发者的APP更方便简单的适配未来不一样苹果设备的不一样大小屏幕。ios

首先咱们来看一下APPLE官方是如何描述Auto Layout的:Auto Layout 是一个系统,可让你经过建立元素之间关系的数学描述来布局应用程序的用户界面,是一种基于约束的,描述性的布局系统。因此咱们如今要开始摒弃使用传统的 设置 frame 的布局方式的思惟来开发视图界面了。由于在 Auto Layout 中,当你描述完视图对象之间的约束以后, Auto Layout 会自动帮你计算出视图对象的位置和大小,也就间接的设定了视图的Frame。反过来,若是咱们还使用传统的绝对定位的方式,经过设定视图的Frame来布 局的话,那么随着苹果设备屏幕尺寸的碎片化,那么每一种屏幕尺寸都要给界面控件设定一套合适该尺寸的Frame,这种方式想一想就够吓人的!另外还须要说明 的是,现在确实还有很多人仍然使用设定Frame的方式进行布局,而且经过取设备屏幕的宽高进行必定比例的换算确实能够达到正确的定位布局,可是在大多数 状况下,若是页面支持屏幕旋转的话,这种设定Frame的方式就彻底失效了,旋转屏幕须要作大量的额外处理。还有一点是咱们必须注意的,不少人习惯上是在 viewdidload方法中初始化控件(包括init 和设置frame),可是viewController要在viewWillLayoutSubviews的时候才能真正肯定view和子view的 frame。因此在没有肯定view的frame的时候就去操做修改view的frame是不友好的,设置frame的效果也是没法预料的。因此按照这个 特性,若是咱们用设置Frame的方式布局的话就必须在viewWillLayoutSubviews中从新设定view的坐标大小,布局逻辑会变得更不 清晰。而使用AutoLayout则不会有这些问题。git

那么接下来咱们来说一下如何使用AutoLayout。github

你们都应该清 楚,咱们能够在XIB、StoryBoard中经过拉线的形式给控件视图添加布局约束,经过苹果强大的可视化界(Interface Builder)咱们可以轻松的使用AutoLayout完成界面视图的布局。另一种方式就是经过纯代码的形式使用AutoLayout,即 NSLayoutConstraint。本人是个代码控,我的比较倾向于代码写界面,因此本文主要讲一下最近本人经过纯代码的方式使用 AutoLayout和使用第三方界面布局库Masonry进行代码布局的总结和分享。数组

首先谈一下在现在AutoLayout的时代,是使用XIB、StoryBoard好些仍是使用纯代码布局好!?本人根据本身的经验以为,这个没有一个绝对的界限或者什么一刀切。可是在权衡这个问题的时候,我我的以为有几个原则应该要去遵照的:框架

一、在一些比较简单、固定的界面。好比登陆、注册或者其余只是进行内容展现的界面使用XIB、StoryBoard开发起来会更简单快一些,这个时候咱们也应该使用XIB、StoryBoard开发。函数

二、 在一些复杂、控件较多和功能多的界面尽可能使用代码进行布局开发。由于控件多功能复杂的界面若是使用XIB、StoryBoard。那么经过拉线的形式添加 约束布局,你们应该都有经历过,一个XIB里拉满了密密麻麻的约束线,能够确定的是过不了多久连本身都看晕了。若是这个模块要交给第二我的维护,那么这些 密密麻麻的约束线确定是一个让人头疼的问题。由于XIB中约束过多的话,首先可读性是很是差的,带来的后续问题是开发思路不清晰、维护难。布局

三、须要复用的模块尽可能使用代码布局。若是使用XIB、StoryBoard则没法很好的对代码模块进行复用。动画

NSLayoutConstraint 篇ui

进入正题,咱们首先来谈一下如何使用官方提供的API(NSLayoutConstraint)进行代码布局。

谈 到NSLayoutConstraint,你们都有一个不怎么好的感受。哎,能够确定的是APPLE一直在推AutoLayout。只是貌似在可视化的布 局设计(XIB、StoryBoard)下的力度和功夫远比代码布局要大。由于经过APPLE提供的API进行代码布局确实不怎么好用,可是仍是在能够接 受的范围,呵呵!

1、Autoresizing Mask

在使用AutoLayout以前咱们先介绍Autoresizing Mask。

必需要注意的是在使用 Auto Layout 时,首先须要将视图的 setTranslatesAutoresizingMaskIntoConstraints 属性设置为 NO。这个属性默认为 YES。当它为 YES 时,运行时系统会自动将 Autoresizing Mask 转换为 Auto Layout 的约束,这些约束颇有可能会和咱们本身添加的产生冲突。 咱们经常会忘了作这一步,而后引发的约束报错就是这样的:

因此若是你是使用 Xib/StoryBoard 的话,系统会自动帮你把这个属性设置为 NO。经过Interface Builder,打开某个Xib或者StoryBoard,在右侧Show in file inspector里面就能看到Ues Autolayout选项,系统默认将其勾选。以下图:

即在XIB上开启Autolayout后,autoresizingMask就被废弃了。避免了约束冲突的状况。

若是你是经过代码布局的话,在给view添加约束以前,只须要经过代码把view的 setTranslatesAutoresizingMaskIntoConstraints 属性设置为 NO

1

2

[objc] view plaincopy

[view setTranslatesAutoresizingMaskIntoConstraints:NO];

2、NSLayoutConstraint

Auto Layout 中约束对应的类为 NSLayoutConstraint,一个 NSLayoutConstraint 实例表明一条约束。

NSLayoutConstraint有两个方法,咱们主要介绍 constraintWithItem:也是最经常使用的:

1

2

[objc] view plaincopy

+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

这 个API给咱们的第一印象就是参数有点多。其实仔细一看表达的意思无非就是:view1的某个属性(attr1)等于view2的某个属性(attr2) 的值的多少倍(multiplier)加上某个常量(constant)。描述的是一个view与另一个view的位置和大小约束关系。其中属性 attribute有上、下、左、右、宽、高等,关系relation有小于等于、等于、大于等于。须要注意的是,小于等于 或 大于等于 优先会使用 等于 关系,若是 等于 不能知足,才会使用 小于 或 大于。例如设置一个 大于等于100 的关系,默认会是 100,当视图被拉伸时,100 没法被知足,尺寸才会变得更大。

那么下面咱们来看一下,如何运用NSLayoutConstraint进行代码布局。

场景一:

假如咱们设计一个简单的页面。一个子view在父view中,其中子view的上下左右边缘都离父view的边缘40个像素。这个咱们该如何写呢?以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

[objc] view plaincopy

[self.view setBackgroundColor:[UIColor redColor]];

//建立子view

UIView *subView = [[UIView alloc] init];

[subView setBackgroundColor:[UIColor blackColor]];

//将子view添加到父视图上

[self.view addSubview:subView];

//使用Auto Layout约束,禁止将Autoresizing Mask转换为约束

[subView setTranslatesAutoresizingMaskIntoConstraints:NO];

//layout 子view

//子view的上边缘离父view的上边缘40个像素

NSLayoutConstraint *contraint1 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:40.0];

//子view的左边缘离父view的左边缘40个像素

NSLayoutConstraint *contraint2 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:40.0];

//子view的下边缘离父view的下边缘40个像素

NSLayoutConstraint *contraint3 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-40.0];

//子view的右边缘离父view的右边缘40个像素

NSLayoutConstraint *contraint4 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-40.0];

//把约束添加到父视图上

NSArray *array = [NSArray arrayWithObjects:contraint1, contraint2, contraint3, contraint4, nil nil];

[self.view addConstraints:array];

效果如图:

这里讲一下比较容易犯错的地方:

一、添加约束前肯定已经把须要布局的子view添加到父view上了

二、必定要禁止将Autoresizing Mask转换为约束

三、要把子view的约束加在父view上

四、由于iOS中原点在左上角因此使用offset时注意right和bottom用负数

场景二:

子view在父view的中间,且子view长300,高200。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

[objc] view plaincopy

[self.view setBackgroundColor:[UIColor redColor]];

//建立子view

UIView *subView = [[UIView alloc] init];

[subView setBackgroundColor:[UIColor blackColor]];

[self.view addSubview:subView];

//使用Auto Layout约束,禁止将Autoresizing Mask转换为约束

[subView setTranslatesAutoresizingMaskIntoConstraints:NO];

//layout 子view

//子view的中心横坐标等于父view的中心横坐标

NSLayoutConstraint *constrant1 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];

//子view的中心纵坐标等于父view的中心纵坐标

NSLayoutConstraint *constrant2 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];

//子view的宽度为300

NSLayoutConstraint *constrant3 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:300.0];

//子view的高度为200

NSLayoutConstraint *constrant4 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200.0];

//把约束添加到父视图上

NSArray *array = [NSArray arrayWithObjects:constrant1, constrant2, constrant3, constrant4, nil nil];

[self.view addConstraints:array];

效果如图:

这里须要注意的是:

一、若是是设置view自身的属性,不涉及到与其余view的位置约束关系。好比view自身的宽、高等约束时,方法constraintWithItem:的第四个参数view2(secondItem)应设为

nil;且第五个参数attr2(secondAttribute)应设为 NSLayoutAttributeNotAnAttribute

二、在设置宽和高这两个约束时,relatedBy参数使用的是 NSLayoutRelationGreaterThanOrEqual,而不是 NSLayoutRelationEqual。由于 Auto Layout 是相对布局,因此一般你不该该直接设置宽度和高度这种固定不变的值,除非你很肯定视图的宽度或高度须要保持不变。

3、更新/修改约束

Auto Layout 的更新、修改约束操做,也不怎么友好和方便。

先来看一下 NSLayoutConstraint 的API,貌似只有constant属性能够修改,其它都是只读的。因此对于现有约束的修改和更新都是围绕NSLayoutConstraint实例的constant属性展开的。

一、若是你是使用 Xib/StoryBoard 的话,在程序运行时想动态的改变视图的约束,你能够这样作:

约束Constraint也能够像控件同样作IBOutlet连接,经过拖线关联到文件。(事例这里是改变子view相对于父view的top约束)

首先在你的ViewController的头文件里定义一个约束属性:

@property (nonatomic, weak)  IBOutlet NSLayoutConstraint *topConstraint ;

1

2

3

4

[objc] view plaincopy

#import @interface LayoutConstraintViewController : UIViewController

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *topConstraint;

@end

而后在XIB里选中 File's Owner,选中Outlet栏目。将对应的Outlet拖动到View对应Constraint链接起来就能够了。跟button/label作连接一摸同样的。

这样咱们就能够得到view上面的某个具体约束了,而后就能够在文件中对这个约束进行咱们想要的修改。

1

2

3

[objc] view plaincopy

//更新约束

self.topConstraint.constant = 10;

总结来讲就是:拖线关联到文件得到约束,修改约束的constant属性。

二、若是你是使用 Xib/StoryBoard ,可是不想经过上述拉线的方式得到约束而后再去更新它,你还能够采用代码的方式修改约束:

可是不管采用哪一种方式,咱们都要遵循Auto Layout 的约束更新机制:想要更新视图上面的约束,就要先找到对应的约束再去更新它。遍历view上面的全部约束,查找到要更新的约束再进行更新。

因此咱们要像上面同样要先得到top约束。在代码中的体现就是经过约束的标识字段,在其父view的constraints数组中遍历查找。这是由于每一个view的constraints数组中保存的其实是 layout 子view所需的约束的集合。

咱们能够经过下面的辅助函数实现:

1

2

3

4

5

6

7

[objc] view plaincopy

NSArray *constrains = self.view.constraints;

for (NSLayoutConstraint* constraint in constrains) {

if (constraint.firstAttribute == NSLayoutAttributeTop) {

constraint.constant = 10;

}

}

这里只判断了一个标识字段(NSLayoutAttributeTop),可是大多数状况下view上面的约束依赖不会那么简单,因此须要查找判断多个标识字段,才能找到。

三、若是你是使用代码布局,那就用上面2介绍的方法更新约束,或者在添加约束以前先将该约束记录下来,方便后面的更新修改。

4、Auto Layout 关于更新约束的几个方法

setNeedsLayout:告知页面须要更新,可是不会马上开始更新。执行后会马上调用layoutSubviews。

layoutIfNeeded:告知页面布局马上更新。因此通常都会和setNeedsLayout一块儿使用。若是但愿马上生成新的frame须要调用此方法,利用这点通常布局动画能够在更新布局后直接使用这个方法让动画生效。

layoutSubviews:系统重写布局。

setNeedsUpdateConstraints:告知须要更新约束,可是不会马上开始。

updateConstraintsIfNeeded:告知马上更新约束。

updateConstraints:系统更新约束。

这么多方法中,目前我使用比较多的是 layoutIfNeeded 。由于在Auto Layout 实现动画的时候,layoutIfNeeded 方法能够马上生成新的frame特性是一大利器。

5、使用 Auto Layout (NSLayoutConstraint)实现动画

目前貌似还有不少人对于 Auto Layout 的动画实现还不是很了解。毕竟之前咱们处理动画之类的交互大都是和view的frame属性打交道,即便用传统的 animation方法修改view的frame。大体相似于

1

2

3

4

5

6

7

[objc] view plaincopy

CGRect newFrame = view.frame;

newFrame.size.height = 300;

[UIView animateWithDuration:3.0 animations:^{

view.frame = newFrame;

} completion:^(BOOL finished) {

}];

那 么在Auto Layout下咱们又该如何处理相关的动画呢?根据前面说到的Auto Layout布局约束的原理,在某个时刻约束也是会被还原成frame使视图显示,这个时刻能够经过layoutIfNeeded这个方法来进行控制,可 以马上生成新的frame并展现出来,从而实现动画效果。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

[objc] view plaincopy

//start animations

//先根据初始化添加的约束生成最初的frame并显示view

[self.view layoutIfNeeded];

[UIView animateWithDuration:3.0 animations:^{

//遍历查找view的heigh约束,并修改它

NSArray *constrains = self.view.constraints;

for (NSLayoutConstraint* constraint in constrains) {

if (constraint.firstAttribute == NSLayoutAttributeHeight) {

constraint.constant = 300;

}

}

//更新约束  在某个时刻约束会被还原成frame使视图显示

[self.view layoutIfNeeded];

} completion:^(BOOL finished) {

}];

这样咱们就能够经过 AutoLayout 实现传统的animation方法。须要注意的是在调用animation方法以前必定要先调用layoutIfNeeded,这是为了让view先生成最初的frame并显示,不然动画效果会失效。

Masonry 篇

Masonry 是一个轻量级的界面布局框架,拥有本身的描述语法,采用更优雅的链式语法封装自动布局,简洁明了并具备高可读性,并且同时支持 iOS 和 Max OS X。Masonry是一个用代码写iOS或OS界面的库,用官方的说明就是Masonry完成能够代替Auto layout。Masonry的github地址:https://github.com/SnapKit/Masonry

Masonry使用起来很方便和流畅,本人最近开始在新项目中使用框架进行界面布局。亲身的实践感受Masonry确实比APPLE的官方的API(NSLayoutConstraint)好用不少。先来看一下Masonry官方的提供的sample

code:

1

2

3

4

[objc] view plaincopy

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.equalTo(superview).with.insets(padding);

}];

这也是最经常使用的用法,为view设置约束。 看到上面的代码风格,典型的链式语法,流畅易懂。

咱们先来看一下Masonry支持的约束属性:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

[objc] view plaincopy

// 左侧

@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;

// 中心点x

@property (nonatomic, strong, readonly) MASConstraint *centerX;

// 中心点y

@property (nonatomic, strong, readonly) MASConstraint *centerY;

// 文本基线

@property (nonatomic, strong, readonly) MASConstraint *baseline;

这 里须要注意的是:NSLayoutAttributeLeft/NSLayoutAttributeRight 和 NSLayoutAttributeLeading/NSLayoutAttributeTrailing的区别是left/right永远是指左右,而 leading/trailing在某些从右至左习惯的地区会变成,leading是右边,trailing是左边。因此若是涉及到国际化方面,建议仍是 使用 NSLayoutAttributeLeading/NSLayoutAttributeTrailing。

在Masonry中可以添加、修改 Auto layout 约束有三个函数:

[objc] view plaincopy

1

2

3

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

 (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;

 (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;

其中:

mas_makeConstraints 只负责新增约束,Autolayout不能同时存在两条针对于同一对象的约束,不然会报错。(这个方法最经常使用)

mas_updateConstraints 针对上面的状况会更新在block中出现的约束,不会致使出现两个相同约束的状况。

mas_remakeConstraints 则会清除以前的全部约束 仅保留最新的约束。

若是咱们灵活的运用这三个方法,基本就能够应付各类各样的约束布局状况了。

1、添加约束(mas_makeConstraints)

先来看一下Masonry如何实现一个view的简单布局。

场景一:

仍是和上面的例子同样:一个子view在父view中,其中子view的上下左右边缘都离父view的边缘40个像素。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

[objc] view plaincopy

[self.view setBackgroundColor:[UIColor redColor]];

//建立子view

UIView *subView = [[UIView alloc] init];

[subView setBackgroundColor:[UIColor blackColor]];

[self.view addSubview:subView];

//layout 子view

__weak __typeof(self)weakSelf  = self;

[subView mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(weakSelf.view).with.offset(40);//子view的上边缘离父view的上边缘40个像素

make.left.equalTo(weakSelf.view).with.offset(40);//子view的左边缘离父view的左边缘40个像素

make.bottom.equalTo(weakSelf.view).with.offset(-40);//子view的下边缘离父view的下边缘40个像素

make.right.equalTo(weakSelf.view).with.offset(-40);//子view的右边缘离父view的右边缘40个像素

}];

针对上面的布局约束写法,还有更为简洁的写法:

1

2

3

4

5

[objc] view plaincopy

//layout 子view

[subView mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.equalTo(weakSelf.view).insets(UIEdgeInsetsMake(40, 40, 40, 40));

}];

效果以下:

能够很明显的看出,使用Masonry布局不管是代码量仍是语法描述都很简洁易懂。比起前面使用 NSLayoutConstraint 不是好一点半点。

场景二:

子view在父view的中间,且子view长300,高200。

1

2

3

4

5

6

7

8

9

10

11

12

[objc] view plaincopy

[self.view setBackgroundColor:[UIColor redColor]];

//建立子view

UIView *subView = [[UIView alloc] init];

[subView setBackgroundColor:[UIColor blackColor]];

[self.view addSubview:subView];

//layout 子view

__weak __typeof(self)weakSelf  = self;

[subView mas_makeConstraints:^(MASConstraintMaker *make) {

make.center.equalTo(weakSelf.view);//子view在父view中间

make.size.mas_equalTo(CGSizeMake(300, 200));//子view长300,高200

}];

效果以下:

2、更新、修改约束(mas_updateConstraints)

使用Masonry更新约束很是方便简单。

好比须要将上面例子的view的宽度和高修都改成100:

1

2

3

4

5

6

[objc] view plaincopy

//将view的宽度、高度设为100

[subView mas_updateConstraints:^(MASConstraintMaker *make) {

make.width.equalTo(@100);

make.height.equalTo(@100);

}];

3、在使用Masonry中,咱们须要注意几个问题

一、在使用 mas_makeConstraint 方法给view添加约束的时候,必需要肯定该view已经添加到父view上了,即[self.view addSubview:view];不然将会约束报错。这个和使用NSLayoutConstraint同样。

二、Autolayout不能同时存在两条针对于同一对象的约束,不然会报错。只能进行更新修改。

三、 其次对于 equalTo 和 mas_equalTo的区别:mas_equalTo只是对其参数进行了一个BOX操做(装箱) ,所支持的类型除了NSNumber支持的那些数值类型以外就只支持CGPoint、CGSize、UIEdgeInsets,例 如:make.size.mas_equalTo(CGSizeMake(300,400));

对于对象或是多个属性的处理,就使用equalTo,例如:make.size.equalTo(weakSelf.view); make.width.equalTo(weakSelf.view); make.height.equalTo(@30);

四、方法with和and,这连个方法其实没有作任何操做,方法只是返回对象自己,这这个方法的左右彻底是为了方法写的时候的可读性 。

五、由于iOS中原点在左上角因此使用offset时注意right和bottom用负数。

六、Masonry约束是没法更新 NSLayoutConstraint 约束。由于Masonry在更新约束的时候会去遍历查找view上面的约束集,先判断view上的约束的类是否为 MASLayoutConstraint的 类,若是是才会进行更新。因此,若是你是用XIB、StoryBoard拉线添加的约束或者是经过代码方式使用NSLayoutConstraint类添 加的约束都没法在代码里用Masonry的 mas_updateConstraints 方法进行约束更新。Masonry更新约束的部分源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

[objc] view plaincopy

- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {

// check if any constraints are the same apart from the only mutable property constant

// go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints

// and they are likely to be added first.

for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {

if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;

if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;

if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;

if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;

if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;

if (existingConstraint.relation != layoutConstraint.relation) continue;

if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;

if (existingConstraint.priority != layoutConstraint.priority) continue;

return (id)existingConstraint;

}

return nil;

}

4、Masonry实现动画

Masonry的动画实现和NSLayoutConstraint类的动画实现基本一致,都是基于 layoutIfNeeded 方法。传统的 animation方法经过Masonry实现以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

[objc] view plaincopy

[self.view setBackgroundColor:[UIColor redColor]];

//建立子view

UIView *subView = [[UIView alloc] init];

[subView setBackgroundColor:[UIColor blackColor]];

[self.view addSubview:subView];

//layout 子view

__weak __typeof(self)weakSelf  = self;

[subView mas_makeConstraints:^(MASConstraintMaker *make) {

//子view的上边缘离父view的上边缘100个像素

make.top.equalTo(weakSelf.view).with.offset(100);

//子view的左边缘离父view的左边缘50个像素

make.left.equalTo(weakSelf.view).with.offset(50);

//子view的右边缘离父view的右边缘50个像素

make.right.equalTo(weakSelf.view).with.offset(-50);

//子view的高度为1

make.height.equalTo(@1);

}];

//先根据初始化添加的约束生成最初的frame并显示view

[self.view layoutIfNeeded];

//开始动画

[UIView animateWithDuration:3.0 animations:^{

[subView mas_updateConstraints:^(MASConstraintMaker *make) {

make.height.equalTo(@300);

}];

//更新约束  在某个时刻约束会被还原成frame使视图显示

[self.view layoutIfNeeded];

} completion:^(BOOL finished) {

}];

参考:

http://www.cocoachina.com/ios/20150702/12217.html

http://www.cocoachina.com/ios/20141219/10702.html

http://xuexuefeng.com/autolayout/

https://github.com/ming1016/study/wiki/Masonry

相关文章
相关标签/搜索