AutoLayout是什么?
使用一句Apple的官方定义的话 关键词:- 基于约束 - 和以往定义frame的位置和尺寸不同,AutoLayout的位置确定是以所谓相对位置的约束来定义的,比如x坐标为superView的中心,y坐标为屏幕底部上方10像素等
- 描述性 - 约束的定义和各个view的关系使用接近自然语言或者可视化语言(稍后会提到)的方法来进行描述
- 布局系统 - 即字面意思,用来负责界面的各个元素的位置。
AutoLayout和Autoresizing Mask的区别
Autoresizing Mask是我们的老朋友了…如果你以前一直是代码写UI的话,你肯定写过UIViewAutoresizingFlexibleWidth之类的枚举;如果你以前用IB比较多的话,一定注意到过每个view的size inspector中都有一个红色线条的Autoresizing的指示器和相应的动画缩放的示意图,这就是Autoresizing Mask。在iOS6之前,关于屏幕旋转的适配和iPhone,iPad屏幕的自动适配,基本都是由Autoresizing Mask来完成的。但是随着大家对iOS app的要求越来越高,以及已经以及今后可能出现的多种屏幕和分辨率的设备来说,Autoresizing Mask显得有些落伍和迟钝了。AutoLayout可以完成所有原来Autoresizing Mask能完成的工作,同时还能够胜任一些原来无法完成的任务,其中包括:- AutoLayout可以指定任意两个view的相对位置,而不需要像Autoresizing Mask那样需要两个view在直系的view hierarchy中。
- AutoLayout不必须指定相等关系的约束,它可以指定非相等约束(大于或者小于等);而Autoresizing Mask所能做的布局只能是相等条件的。
- AutoLayout可以指定约束的优先级,计算frame时将优先按照满足优先级高的条件进行计算。
Autoresizing Mask是AutoLayout的子集,任何可以用Autoresizing Mask完成的工作都可以用AutoLayout完成。AutoLayout还具备一些Autoresizing Mask不具备的优良特性,以帮助我们更方便地构建界面。
AutoLayout基本使用方法
Interface Builder
最简单的使用方法是在IB中直接拖。在IB中任意一个view的File inspector下面,都有Use Autolayout的选择框(没有的同学可以考虑升级一下Xcode了=。=),钩上,然后按照平常那样拖控件就可以了。拖动控件后在左边的view hierarchy栏中会出现Constraints一向,其中就是所有的约束条件。![](http://ww3.sinaimg.cn/mw690/83bbf18dgw1dwxkfbus7qj.jpg)
选中某个约束条件后,在右边的Attributes inspector中可以更改约束的条件,距离值和优先度等:
![](http://ww2.sinaimg.cn/mw690/83bbf18dgw1dwxklmxul8j.jpg)
对于没有自动添加的约束,可以在IB中手动添加。选择需要添加约束的view,点击菜单的Edit->Pin里的需要的选项,或者是点击IB主视图右下角的
![](http://ww3.sinaimg.cn/mw690/83bbf18dgw1dwxkrarjvmj.jpg)
手动使用API添加约束
创建
iOS6中新加入了一个类:NSLayoutConstraint,一个形如这样的约束- item1.attribute = multiplier ⨉ item2.attribute + constant
1 | [NSLayoutConstraint constraintWithItem:button |
2 | attribute:NSLayoutAttributeBottom |
3 | relatedBy:NSLayoutRelationEqual |
4 | toItem:superview |
5 | attribute:NSLayoutAttributeBottom |
6 | multiplier:1.0 |
7 | constant:-padding] |
添加
在创建约束之后,需要将其添加到作用的view上。UIView(当然NSView也一样)加入了一个新的实例方法:- -(void)addConstraint:(NSLayoutConstraint *)constraint;
- 对于两个同层级view之间的约束关系,添加到他们的父view上
![](http://ww1.sinaimg.cn/mw690/83bbf18dgw1dx3236wmnnj.jpg)
- 对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上
![](http://ww1.sinaimg.cn/mw690/83bbf18dgw1dx3237dsbxj.jpg)
- 对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上
![](http://ww4.sinaimg.cn/mw690/83bbf18dgw1dx32384ardj.jpg)
刷新
可以通过-setNeedsUpdateConstraints和-layoutIfNeeded两个方法来刷新约束的改变,使UIView重新布局。这和CoreGraphic的-setNeedsDisplay一套东西是一样的~Visual Format Language 可视格式语言
UIKit团队这次相当有爱,估计他们自己也觉得新加约束的API名字太长了,因此他们发明了一种新的方式来描述约束条件,十分有趣。这种语言是对视觉描述的一种抽象,大概过程看起来是这样的: accept按钮在cancel按钮右侧默认间距处![](http://ww2.sinaimg.cn/mw690/83bbf18dgw1dx32c2yth4j.jpg)
![](http://ww4.sinaimg.cn/mw690/83bbf18dgw1dx32c3win2j.jpg)
![](http://ww3.sinaimg.cn/mw690/83bbf18dgw1dx32c47ab9j.jpg)
最后使用VFL(Visual Format Language)描述变成这样:
1 | [NSLayoutConstraint constraintsWithVisualFormat:@\\ "[cancelButton]-[acceptButton]\\" |
2 | options:0 |
3 | metrics:nil |
4 | views:viewsDictionary]; |
1 | UIButton *cancelButton = ... |
2 | UIButton *acceptButton = ... |
3 | viewsDictionary = NSDictionaryOfVariableBindings(cancelButton,acceptButton); |
{ acceptButton = ""; cancelButton = ""; }
当然,不嫌累的话自己手写也未尝不可。现在字典啊数组啊写法相对简化了很多了,因此也不复杂。关于Objective-C的新语法,可以参考我之前的一篇WWDC 2012笔记:
WWDC 2012 Session笔记——405 Modern Objective-C
。 在view名字后面添加括号以及连接处的数字可以赋予表达式更多意义,以下进行一些举例:
- [cancelButton(72)]-12-[acceptButton(50)]
- 取消按钮宽72point,accept按钮宽50point,它们之间间距12point
- [wideView(>=60@700)]
- wideView宽度大于等于60point,该约束条件优先级为700(优先级最大值为1000,优先级越高的约束越先被满足)
- V:[redBox][yellowBox(==redBox)]
- 竖直布局,先是一个redBox,其下方紧接一个宽度等于redBox宽度的yellowBox
- H:|-[Find]-[FindNext]-[FindField(>=20)]-|
- 水平布局,Find距离父view左边缘默认间隔宽度,之后是FindNext距离Find间隔默认宽度;再之后是宽度不小于20的FindField,它和FindNext以及父view右边缘的间距都是默认宽度。(竖线'|‘ 表示superview的边缘)
容易出现的错误
因为涉及约束问题,因此约束模型下的所有可能出现的问题这里都会出现,具体来说包括两种:- Ambiguous Layout 布局不能确定
- Unsatisfiable Constraints 无法满足约束
- po [[UIWindow keyWindow] _autolayoutTrace]
- [view constraintsAffectingLayoutForOrientation/Axis: NSLayoutConstraintOrientationHorizontal/Vertical]
- [view hasAmbiguousLayout]
- [view exerciseAmbiguityInLayout]
布局动画
动画是UI体验的重要部分,更改布局以后的动画也非常关键。说到动画,Core Animation又立功了..自从CA出现以后,所有的动画效果都非常cheap,在auto layout中情况也和collection view里一样,很简单(可以参考 WWDC 2012 Session笔记——219 Advanced Collection Views and Building Custom Layouts ),只需要把layoutIfNeeded放到animation block中即可~ 1 | [UIView animateWithDuration:0.5 animations:^{ |
2 | [view layoutIfNeeded]; |
3 | }]; |