一、安装Masonry框架

第一步:创建一个工程‘MasonryDemo’

第二步:在项目文件下,创建一个PodFile文件

touch PodFile

第三步:用vim命令打开PodFile

vim PodFile

第四步:输入以下内容

platform :ios, '14.4'   #系统编号,自己更改
target 'MasonryDemo' do   #'MasonryDemo'工程名字,自己根据自己的工程名字更改
pod 'Masonry'
end

第五步:输入pod install命令,运行结果如下

运行成功会出现一个MasonryDemo.xcworkspace文件,我们打开MasonryDemo.xcworkspace文件进行编写代码运行即可。

二、Masonry基础

2.1 Masonry基础API

mas_makeConstraints()    添加约束
mas_remakeConstraints()  移除之前的约束,重新添加新的约束
mas_updateConstraints()  更新约束,写哪条更新哪条,其他约束不变equalTo()       参数是对象类型,一般是视图对象或者mas_width这样的坐标系对象
mas_equalTo()   和上面功能相同,参数可以传递基础数据类型对象,可以理解为比上面的API更强大
[vi mas_makeConstraints:^(MASConstraintMaker *make) {make.size.mas_equalTo(CGSizeMake(200, 200));make.center.equalTo(self.view);}];

2.2 设置内边距

yellow视图与vi视图大小等大,并且有一些内边距,可以自己自行设计,代码如下:

  [self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(vi).with.offset(10);make.top.equalTo(vi).with.offset(40);make.right.equalTo(vi).with.offset(-10);make.bottom.equalTo(vi).with.offset(-10);}];

另一种写法:

  [self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {// 下、右不需要写负号,insets方法中已经为我们做了取反的操作了。make.edges.equalTo(vi).insets(UIEdgeInsetsMake(40, 10, 10, 10));}];

结果如下:

2.3 大于等于和小于等于某个值的约束

 [self.textLable mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);// 设置宽度小于等于200make.width.lessThanOrEqualTo(@200);// 设置高度大于等于10make.height.greaterThanOrEqualTo(@10);
}];self.textLable.text = @"这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。这是测试的字符串。";self.textLable.numberOfLines = 0;  //可以自己试验一下这行去掉了是什么效果,和更改数字的效果

参考文档:

深入剖析Auto Layout

系统理解 iOS 自动布局

三、VFL布局

3.1 VFL的思想

  • VFL的思想与其他的实现方法有所不同,它更为宏观化,它将约束分成了两块
    • 水平方向(H:)
    • 垂直方向(V:)
  • 也就是说,大家在创建约束的时候,得把水平与垂直方向的约束用字符串一并表达出来,而不是一个一个的添加

3.2 代码解析

首先我们需要创建两个view,一个红view,一个蓝view;这两个view的中间间距是20,距离左边距,下边距,右边距距离也是30,代码如下:

UIView *redView = [[UIView alloc] init];UIView *blueView = [[UIView alloc] init];redView.backgroundColor = [UIColor redColor];blueView.backgroundColor = [UIColor blueColor];redView.translatesAutoresizingMaskIntoConstraints = NO;blueView.translatesAutoresizingMaskIntoConstraints = NO;[self.view addSubview:redView];[self.view addSubview:blueView];NSDictionary *views = NSDictionaryOfVariableBindings(redView,blueView);NSDictionary *spaceMetrics = @{@"space": @30,@"height" : @100};//水平NSString *hRedBlueVFL = \@"H:|-space-[blueView]-space-[redView(==blueView)]-space-|";NSArray *hLaoutConstraints = \[NSLayoutConstraint constraintsWithVisualFormat:hRedBlueVFLoptions:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottommetrics:spaceMetricsviews:views];[self.view addConstraints:hLaoutConstraints];//竖直NSString *vRedBlueVFL = @"V:[blueView(50)]-space-|";NSArray *vLayoutConstraints = \[NSLayoutConstraint constraintsWithVisualFormat:vRedBlueVFLoptions:kNilOptionsmetrics:spaceMetricsviews:views];[self.view addConstraints:vLayoutConstraints];

结果如下:

好的,让我们现在来分析一下代码:

  • 首先我们来介绍一下API
/***  VFL创建约束的API**  @param format  传入某种格式构成的字符串,用以表达想要添加的约束,如@"H:|-margin-[redView(50)]",水平方向上,redView与父控件左边缘保持“margin”间距,redView的宽为50*  @param opts    对齐方式,是个枚举值*  @param metrics 一般传入以间距为KEY的字典,如: @{ @"margin":@20},KEY要与format参数里所填写的“margin”相同*  @param views   传入约束中提到的View,也是要传入字典,但是KEY一定要和format参数里所填写的View名字相同,如:上面填的是redView,所以KEY是@“redView”**  @return 返回约束的数组*/
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;//部分NSLayoutFormatOptions的枚举选项
/*
NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),//左边缘对齐NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),//右边缘对齐NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),//左边缘对齐NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),//右边缘对齐NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),//垂直方向中心对齐NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),//水平方向中心对齐
*/
  • 我们需要一下属性translatesAutoresizingMaskIntoConstraints,该属性是将自动布局更改为Constraints布局,该属性默认值是YES,我们需要手动置为NO
  • 接下我们需要连接一下那两行字符串的意思
//水平
NSString *hRedBlueVFL = \
@"H:|-space-[blueView]-space-[redView(==blueView)]-space-|";//竖直
NSString *vRedBlueVFL = @"V:[blueView(50)]-space-|";
  1. ”H“代表的是水平的样式 ”V“代表的是竖直的样式
  2. space表示的是距离,也可以直接用具体的数字代替,也可以用其他的字符串代替,这都是可以自己定义的,定义的数据可以存到字典里面,传达API里的metrics就可以了
  3. [redView(==blueView)] 这句话代表的意思是redView的size跟blueView的size是一样的
  4. 在”H“的字符串里面写blueView(50):代表的是 blueView的宽是50;在”V“的字符串里面写blueView(50):代表的是blueView的高的50
  5. ”|“ 这个代表的是边界

我们这会同意翻译一下这两句话:

  • @"H:|-space-[blueView]-space-[redView(==blueView)]-space-|"

blueView的左边距的大小是space,redView的右边距的大小是space,blueView与redView的大小相等,并且二者之间的距离为space

  • @"V:[blueView(50)]-space-|"

blueView的高的50,并且距离下边距的距离为space

四、使用Masonry实现动画

点击按钮,视图View可以上下移动,代码如下:

- (void)moveView {[self.view setNeedsUpdateConstraints];[self.view updateConstraintsIfNeeded];if (self.count%2==0) {[UIView animateWithDuration:5 animations:^{[self.animaView mas_updateConstraints:^(MASConstraintMaker *make) {make.top.equalTo(self.view).offset(700);}];[self.view layoutIfNeeded];}];}else {[UIView animateWithDuration:5 animations:^{[self.animaView mas_updateConstraints:^(MASConstraintMaker *make) {make.top.equalTo(self.view).offset(418);}];[self.view layoutIfNeeded];}];}self.count++;}

想要更新约束时添加动画,就需要调用关键的一行代码:setNeedsUpdateConstraints,这是选择对应的视图中的约束需要更新。

对于updateConstraintsIfNeeded这个方法并不是必须的,但是有时候不调用就无法起到我们的效果。但是,官方都是这么写的,从约束的更新原理上讲,这应该写上。我们要使约束立即生效,就必须调用layoutIfNeeded此方法。

问题:为什么要加layoutIfNeeded方法?

我们把[self.view layoutIfNeeded];这代代码注释掉,将self.animaView的信息进行打印,结果如下。

我们发现打印出来的第一条数据,仍是初始化的数据,因为在使用Masonry的时候,默认情况下,设置的约束并不会立即生效。接下来我们将注释解开,结果如下:

我们发现打印出来的第一条数据,已经是我们动画之后的数据了,所以layoutIfNeeded的作用就是使设置控件的约束立即生效。

五、源码解析

5.1 类的作用

类图:

5.1.1 View+MASAdditions类的介绍

该类主要包含两个部分,一部分是View的扩展属性,另一部分是View的扩展方法。后面会介绍扩展方法的逻辑。

5.1.2 MASViewAttribute类的介绍

该类主要包含了三个属性,三个方法。从名字我们大致能猜到,该类是对UIView与NSLayoutAttribute的封装。用一个等式来表示就是:MASViewAttribute = UIView + NSLayoutAttribute + item。在MASViewAttribute中view表示约束的对象,item表示该对象被约束的部分。对于UIView来说该item就是UIView本身。该类中除了两个构造器外还有一个isSizeAttribute方法,该方法用来判断MASViewAttribute类中的layoutAttribute属性是否是NSLayoutAttributeWidth或者NSLayoutAttributeHeight,如果是Width或者Height的话,那么约束就添加到当前View上,而不是添加在父视图上。

5.1.3 MASViewConstraint类的介绍

该类是对NSLayoutConstriant类的进一步封装。MASViewConstraint做的最核心的一件事情就是初始化NSLayoutConstriant对象,并将该对象添加在相应的视图上。因为NSLayoutConstriant在初始化时需要NSLayoutAttribute和所约束的View,而MASViewAttribute正是对View与NSLayoutAttribute进行的封装,所以MASViewConstraint类要依赖于MASViewAttribute类。如上图所示。

5.1.4 MASConstraintMaker类的介绍

该类就是一个工厂类,负责创建MASConstraint类型的对象(依赖于MASConstraint接口,而不依赖于具体实现)。mas_makeConstraints方法中的Block的参数就是MASConstraintMaker的对象。用户可以通过该Block回调过来的MASConstraintMaker对象给View指定要添加的约束以及该约束的值。该工厂中的constraints属性数组就记录了该工厂创建的所有MASConstraint对象。

5.2 点语法

make.top.equalTo(vi).with.offset(40);

在使用Masonry框架,我们会使用很多这种调用的方法,他是如何实现的呢?

在MASConstraint中的top,button等约束的getter的方法都会调用下面的这个方法,而这个方法所做的事情就是用过delegate调用MASConstraintMaker中的constaint这个方法,来根据layoutAttribute创建对应的MASConstraint对象。

我们以maker.top.left.right为例。此处的maker, 就是我们的MASConstraintMaker工厂对象,maker.top会返回带有NSLayoutAttributeTop属性的MASViewConstraint类的对象,我们先做一个转换:newConstraint = maker.top。那么maker.top.left 等价于newConstraint.left,需要注意的是此刻调用的left方法就不在是我们工厂MASConstraintMaker中的left的getter方法了,而是被换到MASViewConstraint类中的left属性的getter方法了。给newConstraint设置代理就是为了可以在MASViewConstraint类中通过代理来调用MASConstraintMaker工厂类的工厂方法来完成创建。

而想offset(40)这种调用方式是如何实现的呢?

- (MASConstraint * (^)(CGFloat))offset {return ^id(CGFloat offset){self.offset = offset;return self;};
}

通过代码,我们发现offset函数的返回值是一个匿名block,这匿名的block有个CGFloat参数,返回值是MASConstraint对象。

总结一下:make.top 会通过delegate的方式创建一个MASConstraint对象,该对象会继续访问equalTo属性,执行equalTo的get方法,会执行get方法里面的block,block的返回值是MASConstraint对象,该对象就会继续通过以上两种方式进行执行。

5.3 对约束方法的源码解析

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

首先我们来看一下mas_makeConstraints方法。

我们通过使用mas_makeConstraints对当前视图进行添加的约束,mas_makeConstraints的返回值是一个数组,该数组存放的是对当前视图添加的所有约束。因为MASViewConstraint是对NSLayoutConstraint的封装,所以该数组存储的是MASViewConstraint对象。

该函数的参数是一个block,该block的作用就是用过block给MASConstraintMaker对象中的MAConstraint属性进行初始化。

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {//关闭自动添加约束,这块是需要手动添加的,默认是YESself.translatesAutoresizingMaskIntoConstraints = NO;//初始化MASConstraintMakerMASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];//给maker中的各种属性赋值,通过block进行值的回调block(constraintMaker);//进行约束的添加,并返回install的约束数组(Array<MAConstraint>)return [constraintMaker install];
}

更新与删除的原理是一样的,就是这两个函数都多了两个属性。mas_updateConstraints中将constraintMaker中的updateExisting设置为YES, 也就是说当添加约束时要先检查约束是否已经被安装了,如果被添加了就更新,如果没有被添加就新添加。而mas_remakeConstraints中所做的事情是将removeExisting属性设置成YES, 表示将当前视图上的旧约束进行移除,然后添加上新的约束。

5.4 工厂类中的install

该工厂类不仅仅创建MASConstraint对象,还负责调用MASConstraint对象的install方法来将相应的约束安装在想要的视图上面。该工厂类中的install函数就是遍历工厂对象所创建所有约束对象并调用每个约束对象的install方法来进行约束的安装。

5.5 MASViewConstraint中install

MASViewConstraint中install方法负责创建MASLayoutConstraint对象,并且将该对象添加到相应的View上。

- (void)install {//如果已经添加过约束,就returnif (self.hasBeenInstalled) {return;}//如果layoutConstraint可以使用“isActive”方法,并且self.layoutConstraint不为nilif ([self supportsActiveProperty] && self.layoutConstraint) {//激活约束self.layoutConstraint.active = YES;//将已激活的约束添加到当前View的已安装约束的数组中[self.firstViewAttribute.view.mas_installedConstraints addObject:self];return;}MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {secondLayoutItem = self.firstViewAttribute.view.superview;secondLayoutAttribute = firstLayoutAttribute;}#pragma -- Mark 使用NSLayoutConstraint添加约束//创建约束MASLayoutConstraint *layoutConstraint= [MASLayoutConstraint constraintWithItem:firstLayoutItemattribute:firstLayoutAttributerelatedBy:self.layoutRelationtoItem:secondLayoutItemattribute:secondLayoutAttributemultiplier:self.layoutMultiplierconstant:self.layoutConstant];layoutConstraint.priority = self.layoutPriority;layoutConstraint.mas_key = self.mas_key;//寻找约束添加的Viewif (self.secondViewAttribute.view) {//寻找两个视图的公共父视图MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.viewmas_closestCommonSuperview:self.secondViewAttribute.view];self.installedView = closestCommonSuperview;} else if (self.firstViewAttribute.isSizeAttribute) {self.installedView = self.firstViewAttribute.view;} else {self.installedView = self.firstViewAttribute.view.superview;}MASLayoutConstraint *existingConstraint = nil;if (self.updateExisting) {existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];}if (existingConstraint) {// just update the constant//更新约束existingConstraint.constant = layoutConstraint.constant;self.layoutConstraint = existingConstraint;} else {//添加约束[self.installedView addConstraint:layoutConstraint];self.layoutConstraint = layoutConstraint;[firstLayoutItem.mas_installedConstraints addObject:self];      //约束所在的View增加被添加的约束}
}

参考文档

iOS开发之Masonry框架源码解析

学习Masonry框架 - iOS相关推荐

  1. iOS开发之Masonry框架-源码解析

    Masonry是iOS在控件布局中经常使用的一个轻量级框架.Masonry让NSLayoutConstraint使用起来更为简洁.Masonry简化了NSLayoutConstraint的使用方式,让 ...

  2. iOS开发之Masonry框架源码解析

    Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁.Masonry简化了NSLayoutConstraint的使用方式,让 ...

  3. iOS开发之Masonry框架源码深度解析

    Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁.Masonry简化了NSLayoutConstraint的使用方式,让 ...

  4. QuartzCore框架-- iOS中的动画

    iOS学习笔记之QuartzCore框架 iOS编程给用户视觉反馈其实都是通过QuartzCore框架来进行的,说白了,所有用户最终看到的显示界面都是图层合成的结果,而图层即是QuartzCore中的 ...

  5. 人工智能深度学习Caffe框架介绍,优秀的深度学习架构

    在深度学习领域,Caffe框架是人们无法绕过的一座山.这不仅是因为它无论在结构.性能上,还是在代码质量上,都称得上一款十分出色的开源框架.更重要的是,它将深度学习的每一个细节都原原本本地展现出来,大大 ...

  6. 腾讯优图开源深度学习推理框架 TNN,助力 AI 开发降本增效

    从学界到工业界,"开源"已经成为AI领域的一个关键词.一方面,它以"授人以渔"的方式为AI构建了一个开放共进的生态环境,帮助行业加速AI应用落地:另一方面,在解 ...

  7. 深度学习推理框架调研总结

    深度学习推理框架 作者介绍 1.移动端深度学习推理框架调研 1.1 小米的MACE(2017) 1.2 阿里的MNN 1.3 腾讯的TNN 1.4 ARM的tengine 1.5 百度的paddle- ...

  8. 如何自学php框架,如何学习php框架

    原标题:如何学习php框架 PHP作为网络开发的强大语言之一,现在应用非常广泛,主要运用于中小型企业.具有开放源代码,跨平台性强,开发快捷,效率高,面向对象,并且简单易学,容易于上手,并且是开源产品. ...

  9. 飞桨深度学习开源框架2.0抢先看:成熟完备的动态图开发模式

    百度飞桨于近期宣布,深度学习开源框架2.0抢先版本正式发布,进入2.0时代.其中一项重大升级,就是推出更加成熟完备的命令式编程模式,即通常说的动态图模式.同时在该版本中将默认的开发模式定为动态图模式, ...

最新文章

  1. C++中一些你不知道的冷知识
  2. 赠人玫瑰,手有余香-期待协作更新机器学习的公益项目
  3. 总结构建子类对象时的顺序
  4. 百度要召集 600 名深度学习开发者搞事情? 首届深度学习开发者峰会等你来
  5. 【深度好文】多线程之WaitHandle--派生-》Mutex信号量构造
  6. hibernate执行插入时候报错: IDENTITY_INSERT 设置为 OFF 时,不能为表 ‘user‘ 中的标识列插入显式值
  7. CLION CMAKE 缺-g 导致断点不执行(无效)
  8. 带你快速玩转canvas——写个折线图
  9. python第七天--文件练
  10. Linux设置免密登录
  11. 加壳后软件报毒解决办法
  12. 免费高清图片网站(国外)
  13. 和君商学院A6选拔赛
  14. b站谈服务器崩溃后其他站点,B站服务器崩溃后,蒙古上单和陈睿一起上了热搜...
  15. ecshop的dwt模板文件
  16. 现代程序设计 homework-10
  17. 浏览器自动化操作(Web Browser Automation)(一)
  18. 竞斗云lite正式发布,就是冲着2亿电竞用户的客厅去的!
  19. testpmd csum engine 测试 checksum hw offload
  20. 什么是ftp,什么是ftp?以及具体使用教程

热门文章

  1. Django学习日记21
  2. InfoWorld的2019年度技术奖获奖者
  3. SIP协议-04 SIP头域
  4. 怎么在微信朋友圈中发布长视频,不妨来看看,怎样将长视频发布到朋友圈
  5. 【技术综述】人脸算法新热点,人脸编辑都有哪些方向,如何学习
  6. css,对z-index的使用
  7. 转载 百度地图API二次开发小经验分享
  8. opencv处理图像开始注意的几点
  9. 关于Linux使用pppd拨号,自动断网,重新拨号功能
  10. 山区地貌图 在某山区(平面区域(0,2800)´(0,2400)内,单位:米)测得一些地点的高程(单位:米)如表1,试作出该山区的地貌图.