Masonry使用介绍
更新布局方法
设置好约束以后,布局是如何更新的呢?
Constraints- (void)updateConstraintsIfNeeded // 立即重新计算约束,如果在这之前addConstraints,就可以更新约束- (void)setNeedsUpdateConstraints // 立即返回,标记说需要改变约束值,在当前update cycle结束后更新之前所有标记过要改变的约束,调用updateConstraints方法Layout- (void)layoutIfNeeded // 立即更新布局,重新计算约束,如果在这之前addConstraints就会立即反应在页面上- (void)setNeedsLayout // 同Constraints,不过是更新布局- (void)layoutSubviews // 布局当前页面的子页面Draw- (void)setNeedsDisplay // 同Constraints,不过是重新渲染
Masonry简介使用前的准备Masonry是一个轻量级的布局框架,它拥有自己的描述语法(采用更优雅的链式语法封装)来自动布局,具有很好可读性且同时支持iOS和Max OS X等。总之,对于侧重写代码的coder,请你慢慢忘记Frame,喜欢Masonry吧。
若是你对于自动布局很熟练的话,再接触这个第三方Masonry很容易上手的,对UI界面显示的控件的约束本质都是相同的,现在呢,我一般都是喜欢在控制器里导入 #import "Masonry.h"之前再添加两个宏,来提高App的开发效率。
//1. 对于约束参数可以省去"mas_"
define MAS_SHORTHAND//2. 对于默认的约束参数自动装箱
define MAS_SHORTHAND_GLOBALS即:需要我们导入的框架与宏如下
//define this constant if you want to use Masonry without the 'mas_' prefix
define MAS_SHORTHAND//define this constant if you want to enable auto-boxing for default syntax
define MAS_SHORTHAND_GLOBALS因为之前开发时都是在xib文件中添加约束,或者代码中计算frame并没有接触过Masonry,现在写篇博客来归纳总结下Masonry的使用和注意点。这篇文章只是简单介绍Masonry,以及Masonry的使用,并且会举一些例子出来。但并不会涉及到Masonry的内部实现
Masonry中的坑:
在使用Masonry进行约束时,有一些是需要注意的。
在使用Masonry添加约束之前,需要在addSubview之后才能使用,否则会导致崩溃。在添加约束时初学者经常会出现一些错误,约束出现问题的原因一般就是两种:约束冲突和缺少约束。对于这两种问题,可以通过调试和log排查。之前使用Interface Builder添加约束,如果约束有错误直接就可以看出来,并且会以红色或者黄色警告体现出来。而Masonry则不会直观的体现出来,而是以运行过程中崩溃或者打印异常log体现,所以这也是手写代码进行AutoLayout的一个缺点。这个问题只能通过多敲代码,积攒纯代码进行AutoLayout的经验,慢慢就用起来越来越得心应手了。
Masonry基础使用
Masonry基础API
mas_makeConstraints() 添加约束.mas_remakeConstraints() 移除之前的约束,重新添加新的约束.mas_updateConstraints() 更新约束equalTo() 参数是对象类型,一般是视图对象或者mas_width这样的坐标系对象mas_equalTo() 和上面功能相同,参数可以传递基础数据类型对象,可以理解为比上面的API更强大width() 用来表示宽度,例如代表view的宽度mas_width() 用来获取宽度的值。和上面的区别在于,一个代表某个坐标系对象,一个用来获取坐标系对象的值Auto Boxing
上面例如equalTo或者width这样的,有时候需要涉及到使用mas_前缀,这在开发中需要注意作区分。
如果在当前类引入#import "Masonry.h"之前,用下面两种宏定义声明一下,就不需要区分mas_前缀。
// 定义这个常量,就可以不用在开发过程中使用"mas_"前缀。
#define MAS_SHORTHAND
// 定义这个常量,就可以让Masonry帮我们自动把基础数据类型的数据,自动装箱为对象类型。
#define MAS_SHORTHAND_GLOBALS
修饰语句
Masonry为了让代码使用和阅读更容易理解,所以直接通过点语法就可以调用,还添加了and和with两个方法。这两个方法内部实际上什么都没干,只是在内部将self直接返回,功能就是为了更加方便阅读,对代码执行没有实际作用。
例如下面的例子:
make.top.and.bottom.equalTo(self.containerView).with.offset(padding);其内部代码实现,实际上就是直接将self返回。
(MASConstraint *)with {return self;}更新约束和布局关于更新约束布局相关的API,主要用以下四个API:
(void)updateConstraintsIfNeeded 调用此方法,如果有标记为需要重新布局的约束,则立即进行重新布局,内部会调用updateConstraints方法(void)updateConstraints 重写此方法,内部实现自定义布局过程(BOOL)needsUpdateConstraints 当前是否需要重新布局,内部会判断当前有没有被标记的约束(void)setNeedsUpdateConstraints 标记需要进行重新布局关于UIView重新布局相关的API,主要用以下三个API:(void)setNeedsLayout 标记为需要重新布局(void)layoutIfNeeded 查看当前视图是否被标记需要重新布局,有则在内部调用layoutSubviews方法进行重新布局(void)layoutSubviews 重写当前方法,在内部完成重新布局操作Masonry示例代码Masonry本质上就是对系统AutoLayout进行的封装,包括里面很多的API,都是对系统API进行了一次二次包装。
typedef NS_OPTIONS(NSInteger, MASAttribute) {
MASAttributeLeft=1 << NSLayoutAttributeLeft,MASAttributeRight=1 << NSLayoutAttributeRight,MASAttributeTop=1 << NSLayoutAttributeTop,MASAttributeBottom=1 << NSLayoutAttributeBottom,MASAttributeLeading=1 << NSLayoutAttributeLeading,MASAttributeTrailing=1 << NSLayoutAttributeTrailing,MASAttributeWidth=1 << NSLayoutAttributeWidth,MASAttributeHeight=1 << NSLayoutAttributeHeight,MASAttributeCenterX=1 << NSLayoutAttributeCenterX,MASAttributeCenterY=1 << NSLayoutAttributeCenterY,MASAttributeBaseline=1 << NSLayoutAttributeBaseline,};常用方法
设置内边距
/**设置yellow视图和self.view等大,并且有10的内边距。注意根据UIView的坐标系,下面right和bottom进行了取反。所以不能写成下面这样,否则right、bottom这两个方向会出现问题。make.edges.equalTo(self.view).with.offset(10);
除了下面例子中的offset()方法,还有针对不同坐标系的centerOffset()、sizeOffset()、valueOffset()之类的方法。*/[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.view).with.offset(10);make.top.equalTo(self.view).with.offset(10);make.right.equalTo(self.view).with.offset(-10);make.bottom.equalTo(self.view).with.offset(-10);}];通过insets简化设置内边距的方式
// 下面的方法和上面例子等价,区别在于使用insets()方法。[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {// 下、右不需要写负号,insets方法中已经为我们做了取反的操作了。make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));}];更新约束
// 设置greenView的center和size,这样就可以达到简单进行约束的目的[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);// 这里通过mas_equalTo给size设置了基础数据类型的参数,参数为CGSize的结构体make.size.mas_equalTo(CGSizeMake(300, 300));}];
// 为了更清楚的看出约束变化的效果,在显示两秒后更新约束。dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[self.greenView mas_updateConstraints:^(MASConstraintMaker *make) {make.centerX.equalTo(self.view).offset(100);make.size.mas_equalTo(CGSizeMake(100, 100));}];});大于等于和小于等于某个值的约束
[self.textLabel mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);// 设置宽度小于等于200make.width.lessThanOrEqualTo(@200);// 设置高度大于等于10make.height.greaterThanOrEqualTo(@(10));}];self.textLabel.text=@"这是测试的字符串。能看到1、2、3个步骤,第一步当然是上传照片了,要上传正面近照哦。上传后,网站会自动识别你的面部,如果觉得识别的不准,你还可以手动修改一下。左边可以看到16项修改参数,最上面是整体修改,你也可以根据自己的意愿单独修改某项,将鼠标放到选项上面,右边的预览图会显示相应的位置。";
textLabel只需要设置一个属性即可
self.textLabel.numberOfLines=0;使用基础数据类型当做参数
/**如果想使用基础数据类型当做参数,Masonry为我们提供了"mas_xx"格式的宏定义。这些宏定义会将传入的基础数据类型转换为NSNumber类型,这个过程叫做封箱(Auto Boxing)。
"mas_xx"开头的宏定义,内部都是通过MASBoxValue()函数实现的。这样的宏定义主要有四个,分别是mas_equalTo()、mas_offset()和大于等于、小于等于四个。*/[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);make.width.mas_equalTo(100);make.height.mas_equalTo(100);}];设置约束优先级
/**Masonry为我们提供了三个默认的方法,priorityLow()、priorityMedium()、priorityHigh(),这三个方法内部对应着不同的默认优先级。除了这三个方法,我们也可以自己设置优先级的值,可以通过priority()方法来设置。*/[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);make.width.equalTo(self.view).priorityLow();make.width.mas_equalTo(20).priorityHigh();make.height.equalTo(self.view).priority(200);make.height.mas_equalTo(100).priority(1000);}];
Masonry也帮我们定义好了一些默认的优先级常量,分别对应着不同的数值,优先级最大数值是1000。static const MASLayoutPriority MASLayoutPriorityRequired=UILayoutPriorityRequired;static const MASLayoutPriority MASLayoutPriorityDefaultHigh=UILayoutPriorityDefaultHigh;static const MASLayoutPriority MASLayoutPriorityDefaultMedium=500;static const MASLayoutPriority MASLayoutPriorityDefaultLow=UILayoutPriorityDefaultLow;static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel=UILayoutPriorityFittingSizeLevel;设置约束比例
// 设置当前约束值乘以多少,例如这个例子是redView的宽度是self.view宽度的0.2倍。[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);make.height.mas_equalTo(30);make.width.equalTo(self.view).multipliedBy(0.2);}];小练习
子视图等高练习
/**下面的例子是通过给equalTo()方法传入一个数组,设置数组中子视图及当前make对应的视图之间等高。
需要注意的是,下面block中设置边距的时候,应该用insets来设置,而不是用offset。因为用offset设置right和bottom的边距时,这两个值应该是负数,所以如果通过offset来统一设置值会有问题。*/CGFloat padding=LXZViewPadding;[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.right.top.equalTo(self.view).insets(UIEdgeInsetsMake(padding, padding, 0, padding));make.bottom.equalTo(self.blueView.mas_top).offset(-padding);}];
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, 0, padding));make.bottom.equalTo(self.yellowView.mas_top).offset(-padding);}];
/**下面设置make.height的数组是关键,通过这个数组可以设置这三个视图高度相等。其他例如宽度之类的,也是类似的方式。*/[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.right.bottom.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, padding, padding));make.height.equalTo(@[self.blueView, self.redView]);}];子视图垂直居中练习
/**要求:(这个例子是在其他人博客里看到的,然后按照要求自己写了下面这段代码)两个视图相对于父视图垂直居中,并且两个视图以及父视图之间的边距均为10,高度为150,两个视图宽度相等。*/CGFloat padding=10.f;[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {make.centerY.equalTo(self.view);make.left.equalTo(self.view).mas_offset(padding);make.right.equalTo(self.redView.mas_left).mas_offset(-padding);make.width.equalTo(self.redView);make.height.mas_equalTo(150);}];
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.centerY.equalTo(self.view);make.right.equalTo(self.view).mas_offset(-padding);make.width.equalTo(self.blueView);make.height.mas_equalTo(150);}];UITableView动态Cell高度
在iOS UI开发过程中,UITableView的动态Cell高度一直都是个问题。实现这样的需求,实现方式有很多种,只是实现起来复杂程度和性能的区别。
在不考虑性能的情况下,tableView动态Cell高度,可以采取估算高度的方式。如果通过估算高度的方式实现的话,无论是纯代码还是Interface Builder,都只需要两行代码就可以完成Cell自动高度适配。
实现方式:
需要设置tableView的rowHeight属性,这里设置为自动高度,告诉系统Cell的高度是不固定的,需要系统帮我们进行计算。然后设置tableView的estimatedRowHeight属性,设置一个估计的高度。(我这里用的代理方法,实际上都一样)
原理:
这样的话,在tableView被创建之后,系统会根据estimatedRowHeight属性设置的值,为tableView设置一个估计的值。然后在Cell显示的时候再获取Cell的高度,并刷新tableView的contentSize。
(void)tableViewConstraints {[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self.view);}];}
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.dataList.count;}
(MasonryTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {MasonryTableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:LXZTableViewCellIdentifier];[cell reloadViewWithText:self.dataList[indexPath.row]];return cell;}
// 需要注意的是,这个代理方法和直接返回当前Cell高度的代理方法并不一样。// 这个代理方法会将当前所有Cell的高度都预估出来,而不是只计算显示的Cell,所以这种方式对性能消耗还是很大的。// 所以通过设置estimatedRowHeight属性的方式,和这种代理方法的方式,最后性能消耗都是一样的。
(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {return 50.f;}
(UITableView *)tableView {if (!_tableView) {_tableView=[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];_tableView.delegate=self;_tableView.dataSource=self;// 设置tableView自动高度_tableView.rowHeight=UITableViewAutomaticDimension;[_tableView registerClass:[MasonryTableViewCell class] forCellReuseIdentifier:LXZTableViewCellIdentifier];[self.view addSubview:_tableView];}return _tableView;}UIScrollView自动布局
之前听很多人说过UIScrollView很麻烦,然而我并没有感觉到有多麻烦(并非装逼)。我感觉说麻烦的人可能根本就没试过吧,只是觉得很麻烦而已。
我这里就讲一下两种进行UIScrollView自动布局的方案,并且会讲一下自动布局的技巧,只要掌握技巧,布局其实很简单。
布局小技巧:
给UIScrollView添加的约束是定义其frame,设置contentSize是定义其内部大小。UIScrollView进行addSubview操作,都是将其子视图添加到contentView上。
所以,添加到UIScrollView上的子视图,对UIScrollView添加的约束都是作用于contentView上的。只需要按照这样的思路给UIScrollView设置约束,就可以掌握设置约束的技巧了。
提前设置contentSize
// 提前设置好UIScrollView的contentSize,并设置UIScrollView自身的约束self.scrollView.contentSize=CGSizeMake(1000, 1000);[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self.view);}];
// 虽然redView的get方法内部已经执行过addSubview操作,但是UIView始终以最后一次添加的父视图为准,也就是redView始终是在最后一次添加的父视图上。[self.scrollView addSubview:self.redView];[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.top.equalTo(self.scrollView);make.width.height.mas_equalTo(200);}];
[self.scrollView addSubview:self.blueView];[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.redView.mas_right);make.top.equalTo(self.scrollView);make.width.height.equalTo(self.redView);}];
[self.scrollView addSubview:self.greenView];[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.scrollView);make.top.equalTo(self.redView.mas_bottom);make.width.height.equalTo(self.redView);}];自动contentSize
上面的例子是提前设置好UIScrollView的contentSize的内部size,然后直接向里面addSubview。但是这有个要求就是,需要提前知道contentSize的大小,不然没法设置。
这个例子中将会展示动态改变contentSize的大小,内部视图有多少contentSize就自动扩充到多大。
这种方式的实现,主要是依赖于创建一个containerView内容视图,并添加到UIScrollView上作为子视图。UIScrollView原来的子视图都添加到containerView上,并且和这个视图设置约束。
因为对UIScrollView进行addSubview操作的时候,本质上是往其contentView上添加。也就是containerView的父视图是contentView,通过containerView撑起contentView视图的大小,以此来实现动态改变contentSize。
// 在进行约束的时候,要对containerView的上下左右都添加和子视图的约束,以便确认containerView的边界区域。[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self.view);}];
CGFloat padding=LXZViewPadding;[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self.scrollView).insets(UIEdgeInsetsMake(padding, padding, padding, padding));}];
[self.containerView addSubview:self.greenView];[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.left.equalTo(self.containerView).offset(padding);make.size.mas_equalTo(CGSizeMake(250, 250));}];
[self.containerView addSubview:self.redView];[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(self.containerView).offset(padding);make.left.equalTo(self.greenView.mas_right).offset(padding);make.size.equalTo(self.greenView);make.right.equalTo(self.containerView).offset(-padding);}];
[self.containerView addSubview:self.yellowView];[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self.containerView).offset(padding);make.top.equalTo(self.greenView.mas_bottom).offset(padding);make.size.equalTo(self.greenView);make.bottom.equalTo(self.containerView).offset(-padding);}];
多个(2个以上)控件的等间隔排序显示首先介绍2个函数/**
axisType 轴线方向fixedSpacing 间隔大小fixedItemLength 每个控件的固定长度/宽度leadSpacing 头部间隔tailSpacing 尾部间隔*///1. 等间隔排列 - 多个控件间隔固定,控件长度/宽度变化
(void)mas_distributeViewsAlongAxis:(MASAxisType)axisTypewithFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacingtailSpacing:(CGFloat)tailSpacing;//2. 等间隔排列 - 多个固定大小固定,间隔空隙变化
(void)mas_distributeViewsAlongAxis:(MASAxisType)axisTypewithFixedItemLength:(CGFloat)fixedItemLengthleadSpacing:(CGFloat)leadSpacingtailSpacing:(CGFloat)tailSpacing;//首先添加5个视图NSMutableArray *array=[NSMutableArray new];for (int i=0; i < 5; i ++) {UIView *view=[UIView new];view.backgroundColor=[UIColor greenColor];[self addSubview:view];[array addObject:view]; //保存添加的控件}
//水平方向控件间隔固定等间隔[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:15 leadSpacing:10 tailSpacing:10];[array makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(50);make.height.equalTo(70);}];
//水平方向宽度固定等间隔[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:70 leadSpacing:10 tailSpacing:10];[array makeConstraints:^(MASConstraintMaker *make) { //数组额你不必须都是viewmake.top.equalTo(50);make.height.equalTo(70);}];
这次分享页面布局的一些写法,中途提到了AutoLayout,会后我决定将很久前挖的一个坑给填起来(还有好多坑就不说了,说了不填更毁形象了)。
可使用的框架首推Masonry,关于为啥选择Masonry看看官方文档就明白https://github.com/SnapKit/Masonry,官方称AutoLayout所有功能Masonry都支持。这次项目界面方面我就全部使用了Masonry。
AutoLayout的一些基本概念
? 利用约束来控制视图的大小和位置,系统会在运行时通过设置的约束计算得到frame再绘制屏幕
? 两个属性Content Compression Resistance(排挤,值越高越固定)和Content Hugging(拥抱),Masonry代码如下
//content hugging 为1000[view setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; //content compression 为250[view setContentCompressionResistancePriority:UILayoutPriorityDefaultLowforAxis:UILayoutConstraintAxisHorizontal];
? multipler属性表示约束值为约束对象的百分比,在Masonry里有对应的multipliedBy函数//宽度为superView宽度的20%make.width.equalTo(superView.mas_width).multipliedBy(0.2);? AutoLayout下UILabel设置多行计算需要设置preferredMaxLayoutWidth
label.preferredMaxWidth=[UIScreen mainScreen].bounds.size.width - margin - padding;
? preferredMaxLayoutWidth用来制定最大的宽,一般用在多行的UILabel中
? systemLayoutSizeFittingSize方法能够获得view的高度
? iOS7有两个很有用的属性,topLayoutGuide和bottomLayoutGuide,这个两个主要是方便获取UINavigationController和UITabBarController的头部视图区域和底部视图区域。//Masonry直接支持这个属性make.top.equalTo(self.mas_topLayoutGuide);AutoLayout关于更新的几个方法的区别
? setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
? layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
? layoutSubviews:系统重写布局
? setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
? updateConstraintsIfNeeded:告知立刻更新约束
? updateConstraints:系统更新约束
Masonry使用注意事项
? 用mas_makeConstraints的那个view需要在addSubview之后才能用这个方法
? mas_equalTo适用数值元素,equalTo适合多属性的比如make.left.and.right.equalTo(self.view)
? 方法and和with只是为了可读性,返回自身,比如make.left.and.right.equalTo(self.view)和make.left.right.equalTo(self.view)是一样的。
? 因为iOS中原点在左上角所以注意使用offset时注意right和bottom用负数。
Masonry适配iOS6、iOS7时的注意点
开发项目时是先在iOS8上调试完成的,测试时发现低版本的系统会发生奔溃的现象,修复后总结问题主要是在equalTo的对象指到了父视图的父视图或者父视图同级的子视图上造成的,所以做约束时如果出现了奔溃问题百分之九十都是因为这个。
Masonry使用范例基本写法
//相对于父视图边距为10UIEdgeInsets padding=UIEdgeInsetsMake(10, 10, 10, 10);[self.avatarView 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);}];
//相对于父视图边距为10简洁写法
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(superview).with.insets(padding);}];
//这两个作用完全一样[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.greaterThanOrEqualTo(self.view); make.left.greaterThanOrEqualTo(self.view.mas_left);}];
//.equalTo .lessThanOrEqualTo .greaterThanOrEqualTo使用NSNumber[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { make.width.greaterThanOrEqualTo(@200); make.width.lessThanOrEqualTo(@400); make.left.lessThanOrEqualTo(@10);}];
//如果不用NSNumber可以用以前的数据结构,只需用mas_equalTo就行[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(42); make.height.mas_equalTo(20); make.size.mas_equalTo(CGSizeMake(50, 100)); make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0)); make.left.mas_equalTo(self.view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));}];
//也可以使用数组[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { make.height.equalTo(@[self.view.mas_height, superview.mas_height]); make.height.equalTo(@[self.view, superview]); make.left.equalTo(@[self.view, @100, superview.mas_right]);}];
// priority的使用[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.greaterThanOrEqualTo(self.view.mas_left).with.priorityLow(); make.top.equalTo(self.view.mas_top).with.priority(600);}];
//同时创建多个约束[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { //让top,left,bottom,right都和self.view一样 make.edges.equalTo(self.view); //edges make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(5, 10, 15, 20)); //size make.size.greaterThanOrEqualTo(self.view); make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50)); //center make.center.equalTo(self.view); make.center.equalTo(self.view).centerOffset(CGPointMake(-5, 10)); //chain make.left.right.and.bottom.equalTo(self.view); make.top.equalTo(self.view);}];
AutoLayout情况计算UITableView的变高高度
主要是UILabel的高度会有变化,所以这里主要是说说label变化时如何处理,设置UILabel的时候注意要设置preferredMaxLayoutWidth这个宽度,还有ContentHuggingPriority为UILayoutPriorityRequriedCGFloat maxWidth=[UIScreen mainScreen].bounds.size.width - 10 * 2; textLabel=[UILabel new];textLabel.numberOfLines=0;textLabel.preferredMaxLayoutWidth=maxWidth;[self.contentView addSubview:textLabel]; [textLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(statusView.mas_bottom).with.offset(10); make.left.equalTo(self.contentView).with.offset(10); make.right.equalTo(self.contentView).with.offset(-10); make.bottom.equalTo(self.contentView).with.offset(-10);}]; [_contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
如果版本支持最低版本为iOS 8以上的话可以直接利用
UITableViewAutomaticDimension在tableview的heightForRowAtIndexPath直接返回即可。tableView.rowHeight=UITableViewAutomaticDimension;tableView.estimatedRowHeight=80; //减少第一次计算量,iOS7后支持
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // 只用返回这个! return UITableViewAutomaticDimension;}
但如果需要兼容iOS 8之前版本的话,就要回到老路子上了,主要是用systemLayoutSizeFittingSize来取高。步骤是先在数据model中添加一个height的属性用来缓存高,然后在table view的heightForRowAtIndexPath代理里static一个只初始化一次的Cell实例,然后根据model内容填充数据,最后根据cell的contentView的systemLayoutSizeFittingSize的方法获取到cell的高。具体代码如下//在model中添加属性缓存高度@interface DataModel : NSObject@property (copy, nonatomic) NSString *text;@property (assign, nonatomic) CGFloat cellHeight; //缓存高度
@end - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { static CustomCell *cell; //只初始化一次cell static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ cell=[tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])]; }); DataModel *model=self.dataArray[(NSUInteger) indexPath.row]; [cell makeupData:model]; if (model.cellHeight <=0) { //使用systemLayoutSizeFittingSize获取高度 model.cellHeight=[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1; } return model.cellHeight;}
动画
因为布局约束就是要脱离frame这种表达方式的,可是动画是需要根据这个来执行,这里面就会有些矛盾,不过根据前面说到的布局约束的原理,在某个时刻约束也是会被还原成frame使视图显示,这个时刻可以通过layoutIfNeeded这个方法来进行控制。具体代码如下
[aniView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.bottom.left.right.equalTo(self.view).offset(10);}]; [aniView mas_updateConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view).offset(30);}]; [UIView animateWithDuration:3 animations:^{ [self.view layoutIfNeeded];}];
发表评论