AutoLayout拾遗

最近开始看WWDC的视频,复习了一下AutoLayout的基础内容,写一点儿总结吧,免得看完又忘。

AutoLayout

AutoLayout的定义

AutoLayout是基于约束的,描述性的布局系统。使用约束来描述布局,view的frames会依据这些约束自动进行计算。
AutoLayout是苹果在iOS6中引入的用来替换之前的“Springs&Struts”布局模型的新布局系统。
“Spring&Struts”是基于frame的布局,它在大部分情况下还是有用的,但是随着4寸iPhone5的发布带来的大屏适配的工作以及在横竖屏切换时经常需要在viewWillLayoutSubviews方法中编写大量布局代码。相比之下,AutoLayout不仅可以完成“Spring&Struts”提供的功能,还提供了其所没有的特性:

  • AutoLayout可以指定任意两个view的相对位置,而不需要像Autoresizing Mask那样需要两个view在直系的view hierarchy中。
  • AutoLayout不必须指定相等关系的约束,它可以指定非相等约束(大于或者小于等),而Autoresizing Mask所能做的布局只能是相等条件的。
  • AutoLayout可以指定约束的优先级,计算frame时将优先按照满足优先级高的条件进行计算。


    约束的优点

如何添加约束

通常我们直接在IB中设置约束,这里着重记录下如何用代码添加约束:
当在代码中创建视图和它们的约束条件时候,一定要记得将 translatesAutoResizingMaskIntoConstraints
属性设置为 NO。

  1. 我们可以使用NSLayoutConstraint的类方法:+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c创建约束对象。
  • 注意:上面方程的等号表示的是相等关系,而不是赋值。当自动布局求解这些方程时,并不是将等式右边的值赋值给等式左边。相反,它同时计算属性 1 和属性 2 的值使它们之间的关系成立。
  1. 使用UIView对象的实例方法:-(void)addConstraint:(NSLayoutConstraint *)constraint 将约束添加到view上。
    将约束添加到view上时要注意:
  • 对于两个同层级view之间的约束关系,添加到他们的父view上


  • 对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上


  • 对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上


  1. 可以通过-setNeedsUpdateConstraints(在下一次绘制循环中触发重新布局)和-layoutIfNeeded(强制系统立即更新视图树的布局)两个方法来刷新约束的改变,使UIView重新布局。这和CoreGraphic的-setNeedsDisplay一套东西是一样的。

布局过程

布局过程
  1. 更新约束:这是自下而上(从子视图到父视图)发生的,它为布局准备好必要的信息,而这些布局将在实际设置视图的 frame 时被传递过去并被使用。你可以通过调用-setNeedsUpdateConstraints来触发这个操作,同时,你对约束条件系统做出的任何改变都将自动触发这个方法。
  2. 布局:这是个自上而下(从父视图到子视图)的过程,这种布局操作实际上是通过设置 frame(在 OS X 中)或者 center 和 bounds(在 iOS 中)将约束条件系统的解决方案应用到视图上。你可以通过调用-setNeedsLayout 来触发一个操作请求,这并不会立刻应用布局,而是在稍后再进行处理。因为所有的布局请求将会被合并到一个布局操作中去,所以你不需要为经常调用这个方法而担心。
  3. 显示:显示器都会自上而下将渲染后的视图传递到屏幕上,你也可以通过调用 -setNeedsDisplay来触发,这将会导致所有的调用都被合并到一起推迟重绘。重写熟悉的 drawRect:能够让我们获得自定义视图中显示过程的所有权。

每一步都是依赖前一步操作的,如果有任何布局的变化还没实行的话,显示操作将会触发一个布局行为。类似地,如果约束条件系统中存在没有实行的改变,布局变化也将会触发更新约束条件。
但是这三步并不是单向的。基于约束条件的布局是一个迭代的过程,布局操作可以基于之前的布局方案来对约束做出更改,而这将再次触发约束的更新,并紧接另一个布局操作。这可以被用来创建高级的自定义视图布局,但是如果你每一次调用的自定义 -layoutSubviews 都会导致另一个布局操作的话,你将会陷入到无限循环的麻烦中去。

控制布局

  1. 通常我们需要在-updateConstraints方法中集中添加约束,并且确保在你的实现中增加任何你需要布局子视图的约束条件之后,调用一下 [super updateConstraints],在这个方法中,你不会被允许禁用何约束条件,因为你已经进入上面所描述的布局过程的第一步了。
    如果稍后一个失效的约束条件发生了改变的话,你需要立刻移除这个约束并调用 -setNeedsUpdateConstraints事实上,仅在这种情况下你需要触发更新约束条件的操作。
  2. 如果你不能利用布局约束条件达到子视图预期的布局,你可以进一步重写 -layoutSubviews,通过这种方式当约束条件系统得到解决并且结果将要被应用到视图中时,你便已经进入到布局过程的第二步了。

Intrinsic Content Size

一些视图依据给定的内容有一个原生的大小,这就称为它们的固有内容尺寸。并不是所有视图都有固有内容尺寸:


为了在自定义视图中实现固有内容尺寸,你需要做两件事:重写 -intrinsicContentSize为内容返回恰当的大小,无论何时有任何会影响固有内容尺寸的改变发生时,调用 invalidateIntrinsicContentSize。如果这个视图只有一个方向的尺寸设置了固有尺寸,那么为另一个方向的尺寸返回 UIViewNoIntrinsicMetric/NSViewNoIntrinsicMetric

注意当为了填充一个空间需要拉伸所有视图时,如垂直摆放的两个textview,如果它们都有一个相同的内容压缩优先级,这个布局就会是有歧义的。自动布局不知道该拉伸哪个视图,这时就应该调整某个textview的content-hugging priority与 compression-resistance priority。

Alignment Rect


自动布局并不会操作视图的 frame,但能作用于视图的 alignment rect,在很多情况下,它们是相同的。你可以通过重写alignmentRectForFrame:frameForAlignmentRect
这两个方法在frame与alignment rect之间转换。
当你要自定义一个控件的时候可以重写alignmentRectInsets方法,这个方法允许你返回相对于 frame 的 edge insets。

Debugging


对于不能确定的布局,可以通过调试时暂停程序,在debugger中输入(lldb)po [[UIWindow keyWindow] _autolayoutTrace]来遍历视图层,检查错误。
检查是否有ambiguity:(lldb)po [view hasAmbiguousLayout]
哪里发生了ambiguous:(lldb)po [view excerciseAmbiguousInLayout]

视图消失时如何检查布局错误

动画

使用AutoLayout来做动画
  • CoreAnimation:
[UIView animateWithDuration:0.5 animations:^{
    [view layoutIfNeeded];
}];

请注意,使用这种方法,你可以对约束条件做出的改变并不局限于约束条件的常量。你可以删除约束条件,增加约束条件,甚至使用临时动画约束条件。由于新的约束只被解释一次来决定新的 frames,所以更复杂的布局改变都是有可能的。
需要记住的是:Core Animation 和 Auto Layout 结合在一起产生视图动画时,自己不要接触视图的 frame。一旦视图使用自动布局,那么你已经将设置 frame 的责任交给了布局系统。你的干扰将造成怪异的行为。

  • 直接对约束条件做动画
    约束条件一旦创建后,只有其常量可以被改变。

UIStackView

StackView是iOS9中提供的一种简单布局控件,剔除了复杂的约束,利用自动布局的强大来布局界面,单个 StackView 由一行或者一列控件组成,StackView 根据设置的对齐,间距和大小属性来决定subviews的位置。
同时,StackView也可嵌套来构建更复杂的布局。


AutoLayout和UIScrollView

在使用scroll view时,不仅要定义它的大小跟位置,还要确定它的内容的大小!可按如下步骤来对scroll view进行布局:


Your layout must fully define the size of the content view (except where defined in steps 5 and 6).To set the height based on the intrinsic size of your content, you must have an unbroken chain of constraints and views stretching from the content view’s top edge to its bottom edge.
If your content does not have an intrinsic content size, you must add the appropriate size constraints, either to the content view or to the content.

AutoLayout和Self-Sizing Table View Cells

在iOS8之后,可以下面这种简单的方式来对动态变化的Table View Cell的高度进行自动计算:

tableView.estimatedRowHeight = 85.0
tableView.rowHeight = UITableViewAutomaticDimension

当然,前提是你的cell中的内容的约束已经从上至下形成一个闭环。AutoLayout可由此可以推算出cell的高度。

参考资料

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

推荐阅读更多精彩内容