Autolayout入门教程
iOS发展到现在,iOS5的占有率已经很低了(估计多数还在使用iOS5系统的用户活跃度也不高吧),因此兼容最低版本iOS6.0也不会损失太多用户。同时,下一代大屏iPhone已经发售了,Autolayout绝对是其中一个重要的界面兼容手段。为了能尽快做好适配好新设备的工作,我相信学习Autolayout这门技术也是必不可少。我作为一个使用了Autolayout大约一周左右的初学者,就在这里记录一下一些要点吧。
一、Autolayout的简要背景
Autolayout这个东西是从iOS6开始才出现的,在没有Autolayout的远古年代,大家是用Autoresizing做一些简单的界面拉伸的。
这里先说一下Autoresizing,这个东西支持width和height的四方向拉伸,也可以对x和y做一些简单的四方向对齐。当然,Autoresizing还是基于绝对坐标的,所以界面上的元素还是需要设置好对应的坐标。支持ib和代码设置,如下图
1self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
可以看到代码其实就是设置UIViewAutoresizing枚举变量,代码也是比较简单的。利用Autoresizing就已经能很好适配了搭载iOS6的iPhone5(有部分界面还是需要多个containerview或者写代码手动调节的,不过并不复杂),那为何Apple还要多此一举推出Autolayout呢?
从实用性来看,应对iPhone5之流,Autoresizing足矣,且有兼容性的优势。但是现在时代变了,Android满地开花的大屏手机(5吋以上才能称作大屏),Apple为了保持自有优势,我认为大屏设备和界面的大一统将会是未来战略(iPhone可以使用原来iPad的专用控件)。这必然导致界面的适配问题,最起码Autoresizing已经显得无力了,同时对于部分需要有横向界面的应用会有更复杂的调整工作。如果不使用Autolayout,改用代码手工适配的话,工作量也不算少,同时会需要大量的魔术数来定位坐标。这完全不是现代界面该有的开发方式,实在太原始了。随着iOS8的到来,Autoresizing的兼容性优势会逐渐消失,事实上要同时兼容4个大版本的iOS,这难度的确不小。
因此,现在开始学习并使用Autolayout是一个不错的时机。除了上述因素,从开发工具来看,对于Autolayout的支持已经比Xcode5之前好了不少。其次,针对Autolayout的开源工具库也已经相对成熟,前人踩过的坑大多都可以找到对应解决方法。所以,我实在想不出一个拒绝Autolayout的理由。
二、Autolayout的基础知识
如果大家对Android有所了解,应该知道他们是通过dp的相对坐标和一些布局来定位控件的(好吧,可能我用词不太正确)。但iOS的Autolayout思路和Android的布局思路,还是有很大的差异。Autolayout的本质是约束,通过设定足够多的约束条件,来得出每一个控件的最终坐标。约束条件之间不能相互矛盾,否则会出错抛出异常。条件之间的矛盾还是比较难描述的,这个我在下面的内容再举例说明。
约束的条件有几种类别:
1、大小(Width、Height、WidthEqually等)
2、间隔(HorizontalSpacing等)
3、对齐(TopEdges、HorizontalCenters等)
在使用Autolayout的界面里,如果控件不添加任何约束的话,还是可以使用绝对坐标定位的。但是,Autoresizing和Autolayout在同一个界面层级是不能共存的,只能选择其一。不过实际操作上,Autolayout要实现某些Autoresizing的功能是完全可以的,但是步骤不一定比以前简单。
对于一个控件来说,从前的定位是通过设置frame的4个变量来控制的。但在Autolayout的世界里,这些绝对的数值已经不那么重要了。Autolayout在运行时计算出这些数值也是需要最少2个约束,大多数情况还需要4个以上。
2个约束的例子:
系统的UIActivityView是固定大小的,所以只需要添加x和y的约束。
3个约束的例子:
UILabel类控件,其长度可以由系统计算文字数量和大小得出,所以width或者height的约束可以省去一个。其余两个约束就是x和y的约束,具体表现为HorizontalSpacing等。
4个约束的例子实际上就是多了上述例子一个width或者height约束。
从这些例子,可以得出一个结论,对于一个控件而言,x和y是不可能避免的约束,所以使用Autolayout的控件最少需要2个约束。当然,大多数情况是4个约束起步,同时还需要控件间的各种约束来构建一个完整的界面。
同时在Autolayout的光环下,基本不再需要手动设置frame。使用Autolayout的控件,frame只是表示这个控件当前的坐标。只有相关的约束有变化或约束的控件发生变化,随之这frame也会变化。这些在InterfaceBuilder中可以很容易体现出来,你可以立即看到控件在任意情况下的变化。
三、Autolayout实践应用
这里的实践以ib为主,我在这里解释一下,设置Autolayout实际上有3种方法,分别是:ib、可视语言和传统代码。我分别说说这些方法的特点:
1、ib
直观简单,所见即所得,实时控制界面变化。系统还会提醒冲突和缺少约束,并给出修改建议(建议通常不是最好)。对于约束相对静态的界面,开发效率高。但在复杂页面中,常常是100+的约束,不是很友好。同时,对界面内的控件复用比较麻烦,造成大量相类似约束。某些控件不能直接往其添加子控件,如UILabel添加一个UIImageView到其中。
2、可视语言
需要编写代码,同时要求对Autolayout的体系比较熟悉。由于其语言样式可以比较容易看出控件间的约束关系,所以还是比传统代码直观,但还是无法预想运行时的变化。一次可以添加多个约束,并在运行时改变约束,但是不能完整实现所有约束功能。同时可视语言也有一些学习成本,没有对应的代码建议,所以需要额外的检查来保证语法正确。
3、传统代码
其实就是调用普通的API,参数比较多,但是可以实现Autolayout的所有功能。从代码量来看,要比可视语言多。这种方法更偏向于类似传统code界面,对于一个100+约束的界面,我想还是挺吃力的。不过,在复用性方面有一定的优势,和可视语言一样,可以在运行时改变约束。此外,使用PureLayout开源库可以在一定程度上简化代码。
作为新手而言,通过ib来学习Autolayout是最友好的,可以很直观地观察界面的变化。刚开始时,最容易出现的问题就是遗漏了添加一些必要约束,造成期望与实际不符。ib能检查出约束是否完整,并给予建议,让新手迅速适应Autolayout的开发。
ib会提示警告和错误,警告一般不会在运行时产生异常,但错误是务必要解决的,因为运行时是一定会有异常的。
出现警告的原因一般是frame没有更新,这个可以直接update一下。
而出现错误的原因则是缺少必要约束或者是约束冲突。前者的解决方法是补全约束,后者的情况有点复杂。所谓的约束冲突就是没有办法同时满足所有约束,从而产生冲突。一般是通过删除一些约束,使所有约束都可以满足,个人不建议通过修改约束的优先级来解决。如非特殊情况,一般都是添加越少约束越好,这样思路越清晰,同时对于运行效率都有一定帮助。从目前经验来看,一般的界面开发,的确是用不到优先级的(楼主目前的经验)。
可以看到这些建议不是唯一的,是选择其中一个来补全约束的。新手可以选择系统的建议,看看补充了什么约束,但长远来说,不建议直接使用系统建议补充的约束。个人认为ib的这些警告和错误对于开发界面来说,是极其高效的,但建议却不是非常智能。特别是补充约束的建议,通常和你心中期望的界面是差距很大的。
对于单个控件,可以直接通过菜单或者右下角来添加约束,
右下角的按钮还能一下添加多个约束
对于控件间的约束则需要按住Command+点击,选中多个控件再添加约束。
约束其实也是对象,因此会出现在坐标的列表
同时,约束也支持outlet
约束为什么也需要outlet?显然是为了调用某些方法,而降低了遍历约束难度。
在ib上拉几个控件,就可以完成了上面的简单实践了。但是,光这些是不足以完成适配不同屏幕的开发。
四、Autolayout的复杂约束
Autolayout中的UILabel类控件大小是自适应的,因此一般不需要指定width和height。而且,固定width和height的这类约束要尽量少用(UIImageView这类还是不反对使用),这类约束一般会限制你的思维。如何多个UILabel并排在同一行,但是行宽有限,同时每个UILabel都有较多的字数。那么应该如何控制某个UILabel可以优先完整显示?这时就要利用抗拉伸和抗压缩的优先级了
还有一个相对特别的属性是比例,支持输入类似“16:9”这类有无穷小数的值。具体应用如下,
这个约束可以使控件跟随父视图的大小按比例变化,有类似于之前Autoresizing的效果。
既然约束也是对象,运行动态改变约束的值,则可以达到更多复杂的控制。为了操作方便,我们可以为一些约束创建outlet。同时在运行时,根据需要动态改变约束的数值或者移除添加约束等,这时,为了操作方便,我使用了一个开源库PureLayout。利用PureLayout就可以很方便地,对约束进行操作,其API要比系统的友好不少。github地址:https://github.com/smileyborg/PureLayout。
暂时发现的坑:
1、在tableView的tableHeaderView使用Autolayout时务必要注意,不要使用严格的约束来限制了整个view的大小,否则运行时十有八九会出错。还有,改变tableView大小时,也有一些错位的情况,暂时无解。tableFooterView的情况未测试,估计应该一样。
2、在iOS6上约束的容错性更差,更容易报异常,例如有两个相同约束,iOS6上会有异常断点,但不会崩溃;iOS7上无任何异常出错。当然存在多个相同约束是不规范的行为,编写约束时应尽量做到用最少的约束去达到最佳的效果。
3、Autolayout也是有性能问题的,目前我在iOS6的iPod Touch4上测试100个左右约束的界面。在刷新tableView和添加视图等操作时,能明显感觉到延迟。经过一系列操作,甚至会出现1分钟以上的延迟,这时无法接受的。可以得出一个结论,Autolayout在约束较多的时候,在低端机器上也是会造成性能瓶颈的。不过,幸运的是,我在iOS7上的4s运行同样的界面,就几乎没有延迟。随着低端设备被淘汰,这个问题会得到缓解的。
最后的一个建议是,不要过早地使用Autolayout优化,因为实际上有些界面使用Autoresizing也是能很好地完成适配工作的。同时,对于tableView这类控件使用Autolayout,需要多加留心。