# ios AutoLayout 技术实践

前言

很久没用autolayout,一直用的masonry,再用autolayout,很生疏,写一篇文章,作为手记。

码字较多,确实令人不想看,希望看到的小伙伴认真读一下.

Demo

masonry

个人比较喜欢标哥关于masonry的见解:博客

sb,xib

具体sb,xib的细节这里就不多了解了。下面推荐一篇文章,看下就行了

iOS进阶—SB和XIB的前世今生

xib 使用

xib 原理

ios开发之xib的详细加载过程

这篇文章不错,至少之前没有了解到这一点

xib 转代码

iOS进阶之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实现,如下:

label文本对齐设置内边距

实践后发现,这种方式内容是显示不全的,这里没有深究,有搞过的朋友,可以留言交流。。。☺

labeledge.png

总结:

uilable 自适应高度,是不带内边距的。如果非要实现上面的方案也有,只是内容显示不全。使用TextView是完美的替代方案。下面会讲textview的用法

补充

iOS开发技巧

如果 nib 或 storyboard 里用了 autoLayout,实际运行顺序是先执行viewDidLoad再执行 autoLayout

定制边框需求

视图的边框需求,在项目中大多会用到,我们一般处理方式为view.layer的操作。但是autolayout如何实现呢?

User Defined Runtime Attritubes:用户定义运行时属性

bordertool.png

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嘛,看下图,不解释。

constraintpriority.png
masonrypriority.png

总结:
在内容自适应高度的布局需求中,这种布局方式也是一大利器。请善用

1:有内边距

2:内容默认吸顶

3:如果需要自适应高度,调整高度自适应,不让滚动即可实现

需求拓展

在实际开发中,textview不能一直随着内容增高,会有一个最大高度。实际开发中,我们一般使用封装好的自定义textview。如下,这里讲autolayout,不过多的了解如何自定义

自定义textview

iOS 使用UITextView计算高度,一行代码搞定

button

通过上面label的练习,大部分单个视图的约束操作都是能应对的。下面以button为实例,了解一下不同状态的练习。

button 基本设置:背景色、背景图片、图片、标题、圆角、状态:正常,高亮,选中,等

推荐好文

多操作,熟能生巧,xib中ui布局都是操作性的

imageview

imageview基本操作这里就略了,跟上面一样,下面说一些进阶的:

imageview添加子视图

没有实际操作过的,并不觉得这里有什么,坑点就是imageview上没法添加子视图。纯代码你也可以实践试试。下面说下xib如何实现:

superimageview.png

imageview 添加子视图:拖拽uiview视图,class 改为imageview,由于是UIView,要在.m中设置图片

总结:
碰到视图嵌套的需求,如果父视图不支持嵌套,可以试试这么干。把UIView转为对应子类型

tableview

终于说到tableview,初入门ios的,感觉tableview有些懵逼吧,什么协议,代理,数据源等一些新名词,整的费解。不过,这里说的是布局,嘿嘿

展示全部内容

tableview本身是可以滚动的,如果cell不多,想展示全部内容共,让tableview根据cell自适应高度。参考下面collectionview部分。demo中有实现。

cell自适应高度

我们实际开发中共,cell自适应高度一般使用的都是三方库:

xib布局对应:UITableView-FDTemplateLayoutCell

masonry布局对应:HYBMasonryAutoCellHeight

推荐标哥相关masonry笔记

这里了解一下,不用三方库,实现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,并约束

2:添加containerview,并约束

scrollviewconstraint.gif

理想情况这里应该正常的啊,然而,看一下约束错误。表示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,二是水平居中,试试看:

scrollviewconstraintsolvehor.gif

3):yposition/height。由于需求是竖直滚动,所以,不能竖直居中,只能约束containerview的height。

scrollviewconstraintsolvevertical.gif

scrollview滑动

以上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.gif

green的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

没有图示,是不是看的云里雾里的。真正实践过的,应该知道我在说什么。看到这里的有兴趣的朋友,一起交流。

需求完善

上面说了想要封装customview。并且vc中自动初始化customview,并且vc中能声明customview中控件的IBOutlet。其实vc和customview能共用customview中控件的交互已经很耦合了。还想在vc中开控制customview中的控件,这还要封装customview干嘛。

上面说封装customview后,在vc中使用,还要手动加载。费劲,这里引入一个三方库技术:XXNibBridge

使用很简单,解决需求也很给力,看demo。

问题:

1、用了XXNibBridge嵌套的view会创建两次,第一次会被释放

2、用了XXNibBridge嵌套的view就不能再用Masonry来做约束

3、vc中不能引入customview的控件。会崩溃,没深入研究

4、loadNibNamed手动加载customview.xib文件,没有父视图。

作者很久没有维护了。如果要使用该库,项目中各种场景还是要验证过再用,不然有坑就尴尬了。一般的使用还是完全可以胜任的

xib 高深用法

推荐好文

这里详细讲解了xib一些高端用法,个人了解较少,就不乱说了

IB_DESIGNABLE

IB 动态渲染。

上文里了解了IB_DESIGNABLE的用法和作用,但是那种方式只是将视图的处理封装到自定义视图中,可以在xib中使用,但是没法动态可视化操作。

可视化操作,动态渲染方案

IBInspectable + IB_DESIGNABLE:可视化动态渲染视图

IBInspectable让支持KVC的属性能够在Attribute Inspector中配置
IB_DESIGNABLE的宏的功能就是让XCode动态渲染出该类图形化界面

坑点:
IBInspectable:
布尔类型:BOOL,不能是Boolean
支持类型:属性支持kvc的:Boolean,Number,String, Localized String, Point, Size, Rect, Range, Color, Image, Nil;

IB_DESIGNABLE:
IBInspectable标记的属性,要重写set方法。才能可视化。。。不能重写get方法。会报错

这里以cornerRadius实践:

IBInspectable.png

IBInspectable标记的属性会在这里显示,可操作。

IBInspectable1.png

User Defined Runtime Attritubes。这里的值,是你操作IBInspectable标记的属性后,这里动态显示,没有操作过的,不会显示。重点是:就算你操作了,也只有IB_DESIGNABLE标记的.m文件重写了set方法的属性有效。

IBInspectable2.png

看到没,这里只操作了radius和color,上面User Defined Runtime Attritubes中就只限是了radius和color

IBDESIGNABLE.gif

总结

一起了解了autolayout的一些基本和进阶用法,autolayout操作都是技能性的,多练习就ok了。这里不能穷举所有的用法,学会自己摸索,根据约束错误提示,练习自己的约束方案和习惯

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容