自动布局最重要的是约束:UI元素间关系的数学表达式。约束包括尺寸、由优先级和阈值管理的相对位置。它们是添加剂,可能导致约束冲突 、约束不足造成布局无法确定 。这两种情况都会产生异常。
使用前:AutoLayout关于更新的几个方法的区别。
setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
layoutSubviews:系统重写布局。
setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始。
updateConstraintsIfNeeded:告知立刻更新约束。
updateConstraints:系统更新约束。
使用
基本使用
mas_makeConstraints:添加约束。
mas_updateConstraints:更新约束、亦可添加新约束。
mas_remakeConstraints:重置之前的约束。
multipler属性表示约束值为约束对象的乘因数, dividedBy属性表示约束值为约束对象的除因数,可用于设置view的宽高比。
// 进行屏幕的适配的时候,往往需要根据屏幕宽度来适配一个相应的高度,在此使用如下约束的方式来进行控件的适配。
[self.topView addSubview:self.topInnerView];。
[self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.height.equalTo(self.topView.mas_height).dividedBy(3);。
make.width.and.height.lessThanOrEqualTo(self.topView);。
make.width.and.height.equalTo(self.topView).with.priorityLow();。
make.center.equalTo(self.topView);。
}];
priorityLow()设置约束优先级。
#define MAS_SHORTHAND_GLOBALS使用全局宏定义,可以使equalTo等效于mas_equalTo。
#define MAS_SHORTHAND使用全局宏定义, 可以在调用masonry方法的时候不使用mas_前缀。
// 这里注意到一个地方,就是当使用了这个全局宏定义之后,发现可以有个类`NSArray+MASAdditions.h`,看了之后发现可以。
self.buttonViews = @[ raiseButton, lowerButton, centerButton ];。
// 之后可以在updateConstraints 方法中。
- (void)updateConstraints {。
[self.buttonViews updateConstraints:^(MASConstraintMaker *make) {。
make.baseline.equalTo(self.mas_centerY).with.offset(self.offset);。
}];
[super updateConstraints]; 。
动态修改视图约束:
// 创建视图约束
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {。
self.animatableConstraint = make.edges.equalTo(superview).insets(paddingInsets).priorityLow();。
]];
// 更改约束 (另一处方法中)
UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);。
self.animatableConstraint.insets = paddingInsets;
[self layoutIfNeeded];。
debug模式:
// 对某个view添加key值。
greenView.mas_key = @"greenView";。
// 或者如下顺序
MASAttachKeys(greenView, redView, blueView, superview);。
// 同样的对每条约束亦可以添加key。
make.height.greaterThanOrEqualTo(@5000).key(@"ConstantConstraint");。
preferredMaxLayoutWidth: 多行label的约束问题。
// 已经确认好了位置
// 在layoutSubviews中确认label的preferredMaxLayoutWidth值。
- (void)layoutSubviews {。
[super layoutSubviews];。
// 你必须在 [super layoutSubviews] 调用之后,longLabel的frame有值之后设置preferredMaxLayoutWidth。
self.longLabel.preferredMaxLayoutWidth = self.frame.size.width-100;。
// 设置preferredLayoutWidth后,需要重新布局。
[super layoutSubviews];。
scrollView使用约束的问题:原理通过一个contentView来约束scrollView的contentSize大小,也就是说以子控件的约束条件,来控制父视图的大小。
// 1. 控制scrollView大小(显示区域)
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {。
make.edges.equalTo(self.view);。
}];
// 2. 添加一个contentView到scrollView,并且添加好约束条件。
[contentView makeConstraints:^(MASConstraintMaker *make) {。
make.edges.equalTo(self.scrollView);。
// 注意到此处的宽度约束条件,这个宽度的约束条件是比添加项。
make.width.equalTo(self.scrollView);。
}];
// 3. 对contentView的子控件做好约束,达到可以控制contentView的大小。
新方法:2个或2个以上的控件等间隔排序。
/**
* 多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值。
* @param axisType 轴线方向。
* @param fixedSpacing 间隔大小。
* @param leadSpacing 头部间隔。
* @param tailSpacing 尾部间隔。
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType。
withFixedSpacing:(CGFloat)fixedSpacing l。
eadSpacing:(CGFloat)leadSpacing。
tailSpacing:(CGFloat)tailSpacing;。
/**
* 多个固定大小的控件的等间隔排列,变化的是间隔的空隙。
* @param axisType 轴线方向。
* @param fixedItemLength 每个控件的固定长度或者宽度值。
* @param leadSpacing 头部间隔。
* @param tailSpacing 尾部间隔。
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType。
withFixedItemLength:(CGFloat)fixedItemLength。
leadSpacing:(CGFloat)leadSpacing。
tailSpacing:(CGFloat)tailSpacing;。
使用方法很简单,因为它是NSArray的类扩展:
// 创建水平排列图标 arr中放置了2个或连个以上的初始化后的控件。
// alongAxis 轴线方向 固定间隔 头部间隔 尾部间隔。
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];。
[arr makeConstraints:^(MASConstraintMaker *make) {。
make.top.equalTo(@60);。
make.height.equalTo(@60);。
}];
约束
可以结合使用。
使用常规的Masonry语法完成布局的代码是这样的。
[redView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.left.equalTo(self.view.mas_left).with.offset(0);。
make.bottom.equalTo(self.view.mas_bottom).with.offset(0);。
make.height.equalTo(@100);。
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.left.equalTo(redView.mas_right).with.offset(0);。
make.bottom.equalTo(self.view.mas_bottom).with.offset(0);。
make.width.equalTo(redView.mas_width).with.offset(0);。
make.height.equalTo(redView.mas_height).with.offset(0);。
}];
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.left.equalTo(blueView.mas_right).with.offset(0);。
make.bottom.equalTo(self.view.mas_bottom).with.offset(0);。
make.right.equalTo(self.view.mas_right).with.offset(0);。
make.width.equalTo(blueView.mas_width).with.offset(0);。
make.height.equalTo(blueView.mas_height).with.offset(0);。
}];
代码中可以看到具体的逻辑还是特别清晰的。可以清楚地了解各个控件间的关系,基本上每一行代表着xib中的一根约束。
并且Masonry支持了一些省略和简写:
如果是两个控件的同一个位置(约束)之间的联系,括号中可以只写以来的控件约束可以省略;
如果约束依赖的是同一个控件,那可以用and将两个约束连在一行写;
如果偏移量offset是0,后面的with.offset(0)可以省略;
将上面的代码最简可以写成如下
[redView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.left.and.bottom.equalTo(self.view);。
make.height.equalTo(@100);。
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.bottom.and.width.and.height.equalTo(redView);。
make.left.equalTo(redView.mas_right);。
}];
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {。
make.bottom.and.width.and.height.equalTo(blueView);。
make.right.equalTo(self.view);。
make.left.equalTo(blueView.mas_right);。
}];
注:对于这种几个模块功能相似位置相近的,建议多包一层父控件,便于整个组件的操作与抽离,并且可以从宏观的角度看项目结构更加清晰。
1 MagicNumber -> autoresizingMask -> autolayout 。
以上是纯手写代码所经历的关于页面布局的三个时期。
在iphone1-iphone3gs时代 window的size固定为(320,480) 我们只需要简单计算一下相对位置就好了。
在iphone4-iphone4s时代 苹果推出了retina屏 但是给了码农们非常大的福利:window的size不变。
在iphone5-iphone5s时代 window的size变了(320,568) 这时autoresizingMask派上了用场(为啥这时候不用Autolayout? 因为还要支持ios5呗) 简单的适配一下即可。
在iphone6+时代 window的width也发生了变化(相对5和5s的屏幕比例没有变化) 终于是时候抛弃autoresizingMask改用autolayout了(不用支持ios5了 相对于屏幕适配的多样性来说autoresizingMask也已经过时了)。
那如何快速的上手autolayout呢? 说实话 当年ios6推出的同时新增了autolayout的特性 我看了一下官方文档和demo 就立马抛弃到一边了 因为实在过于的繁琐和啰嗦(有过经验的朋友肯定有同感)。
直到iphone6发布之后 我知道使用autolayout势在必行了 这时想起了以前在浏览Github看到过的一个第三方库Masonry 在花了几个小时的研究使用后 我就将autolayout掌握了(重点是我并没有学习任何的官方文档或者其他的关于autolayout的知识) 这就是我为什么要写下这篇文章来它的原因。
介绍
Masonry 源码
Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性而且同时支持 iOS 和 Max OS X。
我们先来看一段官方的sample code来认识一下Masonry。
1 [view1 mas_makeConstraints:^(MASConstraintMaker *make) { 2make.edges.equalTo(superview).with.insets(padding); 3 }]; 。
看到block里面的那句话: make edges equalTo superview with insets。
通过链式的自然语言 就把view1给autolayout好了 是不是简单易懂?。
使用
cell的高度可以有两种设置方式。
其一就是你说的 通过masonry等第三方的约束工具类。
首先UItableView的cell高度必须设置为如下。
UITableView.cellHeight = UITableViewAutomaticDimension。
estimatedRowHeight = 44.f(大于即可)。
加约束时注意, 你的控件上下之间和高度都要有约束。最上方约束约束到ContentView的上放,最下方的约束加到ContentView的底部。
运行即可cell自动适应控件内容了。
简单的举个栗子:
你的cell在XIB中有一个UILabel,并设置了。上、下、左、右的约束。并且设置了如上所说的cellHeight和estimatedRowHeight的值。
你自己传入UILabel的文本,会把Cell撑起来的。