# ios AutoLayout 技术实践
前言
很久没用autolayout,一直用的masonry,再用autolayout,很生疏,写一篇文章,作为手记。
码字较多,确实令人不想看,希望看到的小伙伴认真读一下.
masonry
个人比较喜欢标哥关于masonry的见解:博客
sb,xib
具体sb,xib的细节这里就不多了解了。下面推荐一篇文章,看下就行了
xib 使用
xib 原理
这篇文章不错,至少之前没有了解到这一点
xib 转代码
这里xib转纯代码,觉得没多大意义,复杂页面转后,会想吐的。不过了解一下也好,初学者,可以将ib视图转为代码,学习控件如何使用的。
xib 约束
视图约束,不管masonry还是AutoLayout。深入理解约束一词的意思,万变不离其宗。即把一个视图束缚在一个地方,当页面变化时候,视图不会乱跑。视图变化只有“上下左右”四个维度,仔细理解宽高的变化其实也是上下左右中的变化,所以,怎样束缚一个视图?
1:确定origin:{x, y}
2:确定frame:{width, height},或确定右边下边,也相当于确定了宽高。
所有就会有各种组合:
上左下右
上左宽高
左宽centerY高
centerXY宽高
。。。
autolayout
关于autolayout的基本了解,这里不多说,推荐简书-一天一点xib系列
控件集
viewarea.png看这一大片,实际项目中,能用40%就不错了,这篇文章就拣一些常用的了解
显示区
displayarea.png这里能看到左边是视图层次结构和约束,中间是可视化页面,右边是布局操作区。点点看,没多少东西
约束操作区
handlearea.gif开发中,常用的也就右下角那几组约束功能。一直用的都是他们
第一个标签
Update Frames.单一功能,就是恢复视图原布局的。跟git上的reset、svn的revert差不多,理解成返回就行了
第二个标签
Embed In Stack.这是Xcode7在iOS9引入的新功能,它用来统一管理它所有的subView(子视图)上的约束.相当于一个容器view用来统一管理他所有subView的约束,其实普通的UIView也可以作为容器view来管理其subView的约束,我们之前做复杂UI显示逻辑的时候往往也会放一个背景的容器view,stack view就是起到这个作用,意义不是很大,它做的事情UIView也可以做,但是他的优势在于:可以通过设置属性的方式让系统自动添加对其subView的约束,而且该view是不渲染在页面上的,对它设置背景色等属性是无效的...(对android有了解的,这个跟merge差不多)
第三个标签
Align.用于添加多个控件间对齐关系,从上到下依次是左对齐、右对齐、上对齐、下对齐、水平对齐、竖直对齐,这些现在都是灰色的不能选择,只有同时选中多个控件,他们才是可用的,或者先选择一个控件,然后按住control拖动到另一个控件上,就会弹出一个控件对齐的窗口,可以在里面设置两个控件的对齐关系。下面两个是相对于superView设置水平、竖直居中,选中单个控件就可以设置
第四个标签
Add New Constraints.用于对单个控件设置约束,上面的四个框分别填写上下左右的约束,注意,每个框右侧的三角是可以点击出菜单进行选择的,比如有A、B、C三个控件,A、B同在C的左侧,对C设置左侧约束的时是可以选择针对A还是B进行计算的,如果通过auto layout设置的约束与显示的结果不符的时候,可以点击三角检查是不是设置约束的参照对象选错了。
Constrain to margins选项的解释:
当拖动一个控件到另一个控件里时,作为super的控件会有几条参考线(蓝色虚线,如果你使用的硬件是带有Force Touch触控板,且使用的xcode7的时候,拖动到参考线处的时候触控板会轻微震动,发出机械上的一个声音以给你反馈),上下左右四个方向的边缘会有,水平、竖直的中心处会有。
若勾选Constrain to margins实际super与sub之间的参考边缘就是这些参考线,而不是实际的super的frame的边缘,如果我们不勾选的话就是以frame的边缘为参考。
下面的Width、Height是限制自身宽高的选项。
Equal Widths、Equal Heights是与其他控件保持相同的宽高,默认是灰色不可用状态,只有选择两个以上的控件,才可使设置,同样也可以先选中一个控件,按住control拖动,弹出的窗口中也有该选项。Aspect Ratio 是设置自身的宽高比的。
Align选项同样是设置两个以上控件的对齐关系的。
Update Frames一般是用来更新frame的。我们设置的约束如果与当前控件的frame产生冲突的时候就要解决冲突,要么修改约束,要么修改frame,最后使系统可以没有歧义的确定UI布局,有冲突的时候会在xib左边栏的右上角显示警告或错误的标识,我们点击标识,按照系统提供的冲突解决方法就可以解决冲突。
第五个标签
Resolve autoLayout issue.这个标签主要用于重新设置autolayout.这个标签可以作用在选中的view或者是以这个view为父视图的所有view
比如:Update Frame,用于更新UI,比如我们设置了自动适应布局,可以用这个选项来更新它的位置.
Clear Constraint,用于清空所有的约束.
注意
如果我们使用了autoLayout自动布局,那么我们在ViewDidLoad和iOS5之后新加入的ViewWillLayoutSubviews中修改Frame均不能生效.这是因为,ViewWillLayoutSubviews这个方法在ViewDidLoad之后调用,也就是说frame生效之后接着就被autoLayout给重新布局了.
既然这样那么,我们要更改frame就要在ViewDidLayoutSubviews中更改,或者将自动布局拖成属性,在代码中更改.
属性设置区
propertysettingarea.gif这里面绝大部分设置,用纯代码都能编写。所以,这里设置,转为纯代码,去了解学习控件属性知识也是不错的办法
第一个标签
show the file inspector.这个标签主要介绍xib文件的基本信息,一般是不会用到的,所以也不用修改.
第二个标签
show quick help inspector.这个标签就是一些快捷帮助信息,基本上就是苹果API中对某个控件的介绍.
第三个标签
show the identity inspector.在这个标签下主要做一些标识.我们最常用的就是其中的Custom Class,用这个标签来关联xib文件与我们自己创建的类文件
第四个标签
show the attributes inspector.在这个标签使我们最常用的一个标签,我们通常会使用它进行控件的属性设置.比如设置模拟器的一些尺度,颜色等相关的.这个标签的内容(即可设置的属性)会因控件的不同而变化的.
第五个标签
show the size inspector.这个标签是设置frame的相关,主要与尺寸相关.
第六个标签
show the connections inspector.这个标签主要负责xib文件与类的源文件交互,通俗的将就是"连线",在xib中控件的属性与触发的动作,都是可以拖一条线到类的源文件中,用代码进行下步操作的.这会在接下来进行介绍.
实践出真知
下面通过项目中常用的控件约束逐一讲解xib中autolayout的使用
label
一般约束
第一次使用autolayout,先拖一个label试试
这是xib中autolayout布局的一个label,整个操作如图。
labeltry.gif这是用三方约束库masonry进行约束的,看代码,刚设置的background和text用纯代码写都是一一对应的,所以,这里告诉你,xcode的可视化ui布局做的很棒,开发中一般用到的视图属性设置,这里都能找到设置的地方
labelmasonry.png这是用最古老的纯代码编写ui布局。看下代码量,如果写个稍微复杂点的页面,是不是会觉得不爽。
labelframe.png从上面可以看出,纯代码开发的,就不过多讨论了,比较masonry和autolayout布局。用哪个呢?autolayout能快masonry几条街吧。
最后,针对刚入门的ios开发来说,建议先masonry,再autolayout。因为autolayout的实现,你并不知道oc代码的具体实现,不利于修炼内功。(当然,外包的话,你是没有那个条件的,只能用autolayout,只为快,只是重复)
内边距需求
项目中label一般clear背景展示内容,很少有内景色的,但是如果美工设计需要有内边距的。但是autolayout中并没有内边距设置呀?
labelattributed.png看到label的attributed类型是不是有点联想,我们代码实现label的行间距,字体颜色等富文本设置的时候,用的就是这里的一些东西。但是没有找到能设置内边距的。这种需求可以通过自定义label实现,如下:
实践后发现,这种方式内容是显示不全的,这里没有深究,有搞过的朋友,可以留言交流。。。☺
labeledge.png总结:
uilable 自适应高度,是不带内边距的。如果非要实现上面的方案也有,只是内容显示不全。使用TextView是完美的替代方案。下面会讲textview的用法
补充
如果 nib 或 storyboard 里用了 autoLayout,实际运行顺序是先执行viewDidLoad再执行 autoLayout
定制边框需求
视图的边框需求,在项目中大多会用到,我们一般处理方式为view.layer的操作。但是autolayout如何实现呢?
bordertool.pngUser Defined Runtime Attritubes:用户定义运行时属性
key path。有些感触吧,oc的kvc编码,所以,对象的属性都可以在这里试试看。只是有些特例无法直接实现,如boder.color。。
注意: runtime,看到了吧,所以,xib中是无法可视化的。运行起来才能看到效果。
特例:设置边框颜色
layer.borderColor type里只有Color 没有CGColor。
解决:CALayer分类 提供方法:transeColor2CGColor:(UIColor *)color。方法写不写在.h里面都无所谓
使用:layer.transeColor2CGColor
注意:正常来讲,分类里是没有属性的,但是在使用时候,就当有属性使用,所以,方法名一般为setAbc。然后xib中设置时候使用是abc,如下:
CALayer+Color.png layerbordercolor.png实际操作时候,尽量还是用copy,手敲的话,很容易手误,又很难发现。如果设置错误,系统默认为黑色。
textview
高度自适应需求
针对label,textview等需要根据文本内容,高度自适应,可以使用纯代码计算内容高度,但是大家应该也比较诟病这种方法,诸如开发麻烦,计算不准等。使用autolayout相对来说有种方案就简单多了,如下:
textviewadjustheight.gif看效果:
label2textview.png这里有两点注意:
1:设置过后,看上图,高度约束变虚线了。
2:用过masonry都知道,每条约束都有优先级,因为masonry也是封装的NALyoutConstraint嘛,看下图,不解释。
总结:
在内容自适应高度的布局需求中,这种布局方式也是一大利器。请善用
1:有内边距
2:内容默认吸顶
3:如果需要自适应高度,调整高度自适应,不让滚动即可实现
需求拓展
在实际开发中,textview不能一直随着内容增高,会有一个最大高度。实际开发中,我们一般使用封装好的自定义textview。如下,这里讲autolayout,不过多的了解如何自定义
button
通过上面label的练习,大部分单个视图的约束操作都是能应对的。下面以button为实例,了解一下不同状态的练习。
button 基本设置:背景色、背景图片、图片、标题、圆角、状态:正常,高亮,选中,等
多操作,熟能生巧,xib中ui布局都是操作性的
imageview
imageview基本操作这里就略了,跟上面一样,下面说一些进阶的:
imageview添加子视图
没有实际操作过的,并不觉得这里有什么,坑点就是imageview上没法添加子视图。纯代码你也可以实践试试。下面说下xib如何实现:
superimageview.pngimageview 添加子视图:拖拽uiview视图,class 改为imageview,由于是UIView,要在.m中设置图片
总结:
碰到视图嵌套的需求,如果父视图不支持嵌套,可以试试这么干。把UIView转为对应子类型
tableview
终于说到tableview,初入门ios的,感觉tableview有些懵逼吧,什么协议,代理,数据源等一些新名词,整的费解。不过,这里说的是布局,嘿嘿
展示全部内容
tableview本身是可以滚动的,如果cell不多,想展示全部内容共,让tableview根据cell自适应高度。参考下面collectionview部分。demo中有实现。
cell自适应高度
我们实际开发中共,cell自适应高度一般使用的都是三方库:
xib布局对应:UITableView-FDTemplateLayoutCell
masonry布局对应:HYBMasonryAutoCellHeight
这里了解一下,不用三方库,实现cell自适应高度的操作:self-sizing cell,实在不想码字了,看demo实践
collectionview
如何显示collectionview全部内容这里有个论题,可以看看。实践发现,方案如下:
方案:
collectionView的contentSize.height赋给collectionView的高度约束。
总结:
collectionview作为复杂页面的子视图,如果需要展示全部cell,这种方案是比较好的。tableview也同样道理。如果有需求,就可以这么干。。
1:设置collectionview不可滚动
2:设置collectionview高度约束
注意:获取contentSize一定要在cell加载完成后,不然获取到为0。collectionview是不会显示的,cell也不会加载。
scrollview
开发中,有些页面,无规律没法用tableview,但是有很长,超过了屏幕,我们首选scrollview。说到scrollview,感觉是比较难用的,特别是masonry布局时候,有些麻烦。这里了解一下,提供一个不错的方案。
scrollview约束
因为scrollview是可以滚动的,所以有个自身的frame,还有个并不存在的contentview,就是scrollview里的子视图们。想象一下放映机,放映口和胶片。放映口相当于scrollview的frame,胶片相当于scrollview内的内容。一旦胶片长度超过放映口。是不是就有滑动的观感了。tableview和collectionview和textview都是继承自scrollview,所以,都是同样的道理。如何实现?原理就是scrollview添加containerview。子视图们撑开containerview,将constainerview的size作为scrollview的contentsize。
目的:确定 scrollview 的 contentSize
三步:
1:添加scrollview,并约束
scrollviewconstraint.gif2:添加containerview,并约束
理想情况这里应该正常的啊,然而,看一下约束错误。表示scrollview没法确定x方向的position或width,和y方向的position或height。
3:约束解决
我们目的是确定scrollview的contentsize。那么即是确定containerview的size{width,height}和origin{x,y}。如下:
1):width。同scrollview的frame的width即可。已经约束过
2):xposition/width。假设需求是竖直滚动。那么设置水平居中是最合理的。因为scrollview中content的左右相对约束点你都不知道。containerview的左右相对scrollview已经约束过,怎样组合,水平方向才能束缚住containerview不乱跑呢?一是设置containerview的width,二是水平居中,试试看:
3):yposition/height。由于需求是竖直滚动,所以,不能竖直居中,只能约束containerview的height。
scrollviewconstraintsolvevertical.gifscrollview滑动
以上scrollview的约束做过了。知道原理,scrollview以什么样的frame存在或嵌套都是可以应对的。西面了解下开发中常见的场景:
subviews超过一屏,使用scrollview实现滚动:
方案:手动计算更新containerview的height约束。
这种虚拟器窗口不能变动
simulatedmetricsinferred.png
调整metrix
simulatedmetricsfreeform.png
调整虚拟窗口,拉大高度
simulatedmetricsscroll.png
手动计算-动态设置containerview的高度
handlecontainerviewheight.png控件平分
上面了解了单个视图的布局。下面了解一下多视图组合布局的技能。
需求:视图等宽等间距平分父视图
viewdivide.gif比例布局
实际开发中,比例布局用的感觉不是很多。这里也了解一下:
multiplier.gif aspectratio.gif这个第二类约束,解释一下:
约束过红view的宽高ratio后,约束报错,是因为已经约束了宽高,现在又约束宽高比1:2,跟原宽高比不一样,所以,删除宽高的任一约束即可。
组合批量处理
先看个错误的栗子。
grouphandleerror.gifgreen的view为啥跑到前面去了呢?而不是4个view左右平分。看下green视图的相对约束,发现是相对于safe area。为啥呢 ?因为干刚开始放的时候,green视图就非常靠上,该视图左右能看到的第一个视图就是safe area。所以,就这样了。这也是autolayout方便的一点,默认视图依赖,下面看正确的处理。。
grouphandle.gif解释一下:组合批量处理分3个步骤:
1:预先处理
上面错误例子演示了,首先要把几个视图放到差不多的位置,然后再进行整体处理
2:划整体处理
将视图组处理为一个整体:
1:水平对齐
2:左右平分,等宽等高
3:整体约束
上面将视图化为一个整体了,剩下的,就单选任一个视图,确定yposition/height即可。这里操作的是blue视图,topSpace和height。这样就确定了每个视图的上下左右或宽高四个约束了
组合自适应布局
这种布局的场景一时没想起来,没遇到过。这里就不多了解了。推荐一篇好文:
子视图撑开父视图
这类布局需求太常见了,总想有骚操作来实现,实际开发中总是用笨方法,计算子视图高,更新父视图高。这一点都不智能啊:用autolayout下面有一种方法,一起了解一下:
groupadjust.gif解释:
高度自适应
设置约束,记住规则:先父后子,父无高需自适,子一一约束四方,最后父依赖最底视图的底部。
图中的子视图,由于自适应内容,所以特殊设置了高,priority。上面label,textview有了解过。
priority设置多大?
系统提供的有Required:1000,High:750,Low:250。这里设置时候取值范围:0 < x < 750 & x != 250。
看效果:
groupadjustresult.gif基于Object封装
推荐文章。。这里有了解object的使用。我也是照着实践的,所以,就不搬砖了
基于UIView封装
基于UIView封装xib: xib 还没那么强大:
1:如果想实现vc.xib自动初始化customview。那么,vc和view就没法共用view中的交互
封装自定义view步骤
1):customview.xib不设置同名class
2):customview.xib设置file's owner为同名cutomview类
3):在vc.xib直接使用view视图,设置class为customview即可
2:如果想实现vc和customview共用customview中的交互。那么,vc中需要手动加载customview.xib。并约束。
封装自定义view步骤
1):customview.xib设置同名class
2):customview.xib设置file's owner为所在vc类
3):在vc中加载customview.xib。按需约束。
1):约束不能和vc.xib统一约束,可以masonry和autolayout共同约束,所以,一般可用在单一约束上,没有其他customview的相对约束,比如:tableview的headerview。
2):customview.xib不能放到customview中initwithcoder进行初始化加载。只能使用的地方手动加载。因为:customview.xib的file's owner是vc
没有图示,是不是看的云里雾里的。真正实践过的,应该知道我在说什么。看到这里的有兴趣的朋友,一起交流。
xib 高深用法
这里详细讲解了xib一些高端用法,个人了解较少,就不乱说了
总结
一起了解了autolayout的一些基本和进阶用法,autolayout操作都是技能性的,多练习就ok了。这里不能穷举所有的用法,学会自己摸索,根据约束错误提示,练习自己的约束方案和习惯