MENU

谈谈 iOS 的 AutoLayout

AutoLayout简介

iOS 开发中一般有两种布局方式:

  • Frame + Autoresing布局

  • AutoLayout布局

我们知道,在二维坐标系中,元素的位置信息由其在坐标系中原点的X,Y值决定。元素的尺寸信息则由Width,Height值确定。Frame + Autoresizing布局就是通过设置视图的左上角原点位置(x,y)和尺寸大小(width,height)来设置其布局。再通过Autoresizing来维持其与父视图的关系。这些关系如下

枚举值效果
UIViewAutoresizingNoneview的frame不会随superview的改变而改变
UIViewAutoresizingFlexibleLeftMargin自动调整view与superview左边的距离保证右边距离不变
UIViewAutoresizingFlexibleWidth自动调整view的宽,保证与superView的左右边距不变
UIViewAutoresizingFlexibleRightMargin自动调整view与superview右边的距离保证左边距不变
UIViewAutoresizingFlexibleTopMargin自动调整view与superview顶部的距离保证底部距离不变
UIViewAutoresizingFlexibleHeight自动调整view的高,保证与superView的顶部和底部距离不变
UIViewAutoresizingFlexibleBottomMargin自动调整view与superview底部部的距离保证顶部距离不变

但是这种布局方式无法处理兄弟视图之间的关系,更无法反向进行布局处理。

Auto Layout dynamically calculates the size and position of all the views in your view hierarchy, based on constraints placed on those views.

在iOS6之后,苹果推出了AutoLayout。根据苹果的官方文档描述,AutoLayout会动态的根据我们对视图设置的约束来计算视图的尺寸和位置。AutoLayout允许程序员用描述性的语言来决定视图之间的布局关系。
AutoLayout布局

// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Red.top = 1.0 * Blue.top + 0.0
Red.bottom = 1.0 * Blue.bottom + 0.0
 
//Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0

注意,AutoLayout的约束设置必须要完整,如果不完整则会产生一些未知错误导致无法完成布局。同时,如果约束设置过多,AutoLayout就会选择断开其中某些约束,直到能够正确布局。然后再根据未断开的约束来计算布局。同时也可以给约束设置优先级,AutoLayout就会根据优先级(1-1000)来确定约束的有效性。

AutoLayout工作原理

AutoLayout之所以能够通过描述性的语言来确定界面布局关系,其核心是Cassowary算法。AutoLayout会将设置好的描述性语言转化成类似下方的线性不等式来求解,最终利用Frame来进行布局。

a[1]x[1] + ... + a[n]x[n] = b,

a[1]x[1] + ... + a[n]x[n] <= b, or

a[1]x[1] + ... + a[n]x[n] >= b,

AutoLayout工作流程

Intrinsic content size

除了用约束来定义界面的位置和尺寸外,一些界面还具有自己的固有尺寸。但是并不是所有的界面都有固有尺寸。

ViewIntrinsic content size
UIView and NSViewNo intrinsic content size.
Sliders<div>Defines only the width (iOS).</div><div>Defines the width, the height, or both—depending on the slider’s type (OS X).</div>
Labels, buttons, switches, and text fieldsDefines both the height and the width.
Text views and image viewsIntrinsic content size can vary.

什么是固有尺寸呢?如果我们想对一个UILabel进行布局,如果不使用AutoLayout,我们需要先设置其文字,然后根据字体、字体大小来计算Size,然后设置Frame值。如果利用AutoLayout进行布局,设置其上、左约束后,Label就能正常显示。

 override func viewDidLoad() {
        super.viewDidLoad()
        let label = UILabel()
        label.text = "Helloooooooooooo,Worlllllllllllllllld!"
        label.textColor = .white
        label.backgroundColor = .gray
        label.font = UIFont.systemFont(ofSize: 14.0)
        self.view.addSubview(label)
        label.snp.makeConstraints { (make) in
            make.left.equalToSuperview().offset(10)
            make.top.equalToSuperview().offset(40)
        }
    }

代码中并没有设置过UILabelSize信息,可是Label却能够正确显示。这其中必不可少的一个API就是:intrinsicContentSize方法。这个方法可以获取到界面元素的固有尺寸,而AutoLayout设置了界面的位置关系。尺寸、位置信息都具备,所以界面就能够正常显示。
label

intrinsicContentSize

比较有疑问的是:为什么通过intrinsicContentSize + AutoLayout位置约束 = 正确的界面显示?

AutoLayout计算好尺寸之后通过intrinsicContentSize暴露给外界还是AutoLayout通过intrinsicContentSize获取已经计算好的界面尺寸来进行布局?
为了验证这个问题,去掉了AutoLayout,转为只设置LabelOrigin信息,然后通过intrinsicContentSize设置其大小。

    label.backgroundColor = .cyan
    label.frame.origin = CGPoint(x: 10, y: 40)
    label.frame.size = label.intrinsicContentSize

验证猜测
发现,Label也能照常显示,那么说明AutoLayout并不会计算尺寸,而是通过intrinsicContentSize获取尺寸,然后再加上已经设置好的约束信息,转换为Frame,进行布局。

Content hugging and compression

除此之外,AutoLayout还会用一对约束来展示界面的intrinsicContentSize
可压缩和可拉伸约束
其中Content hugging指可拉伸约束(默认优先级250),Content compression(默认优先级750)指抗压缩约束,二者都区分水平或者竖直方向。当需要界面保持固有的宽度或高度,可以提升其抗压缩约束优先级;当需要界面能够被压缩或拉伸变化,可以改变其可拉伸约束优先级。

Content hugging

Content hugging个人理解是指在除开固有内容的情况下,Label可以被拉伸的力。优先级越高,拉伸力越小。

  • 默认情况当Label1Label2都没有设置抗压缩和可挤压约束,会发现Label1被拉伸。
    当没有设置可压缩和可拉伸约束的时候

  • 当提升Label1的可拉伸约束优先级的时候,拉伸力变小,Label1则回到原型,Label2被拉伸。
    提升Label1的可拉伸优先级

  • 为了达到同样的目的,我们也可以不改变Label1,转至降低Label2的可拉伸优先级。优先级低,力越大,Label1则回到原型,Label2被拉伸。

Content compression

Content compression个人理解是指在有内容的情况下,内容的抵抗力,优先级越高,抵抗力越强。为了试验,加长了两个Label的内容长度,默认情况下,Label2被压缩。

  • 当提升Label2的抗压缩优先级,其抵抗力增强,Label1内容被压缩

  • 为了达到同样的目的,降低Label1的抗压缩优先级,其抵抗力比Label2小,内容被压缩。

preferredMaxLayoutWidth

intrinsicContentSize一起使用的还有preferredMaxLayoutWidth,官方解释如下:

This property affects the size of the label when layout constraints are applied to it.

这个属性主要用来设置元素的最大宽度(point单位),当利用intrinsicContentSize根据内容计算尺寸的时候,系统会根据preferredMaxLayoutWidth预设的最大宽度来计算UILabel的高度。当这个值没有设置的时候,UILabel的换行就会以它自己本身的宽度来进行换行适配。如果二者都没有设置,则会根据字体来设置一个默认的高度,并且不会进行换行。如果二者都设置了值,那么高度的计算以preferredMaxLayoutWidth为准。同时要记得将numberOfLines设置为想要的行数

<figure class="half">

<img src="http://op3un88z7.bkt.clouddn.com/2017-09-22-052356.jpg">
<img src="http://op3un88z7.bkt.clouddn.com/2017-09-22-052829.jpg">

</figure>

    label.frame.origin = CGPoint(x: 10, y: 40)
    label.preferredMaxLayoutWidth = 50
    label.numberOfLines = 0
    label.frame.size = label.intrinsicContentSize

<code>preferredMaxLayoutWidth</code>

Autolayout的性能分析

我们知道,AutoLayout布局计算需要解方程组,就会占用CPU,如果大量的使用AutoLayout进行布局性能消耗和直接使用Frame布局比起来怎么样?
下面在简单布局和复杂布局的情况下对Autolayout布局和Frame布局进行比较。测试机器是iPhone5S,iOS 10.3.3系统。
简单布局
图中纵坐标为屏幕刷新率,横坐标为布局视图个数。由于视图个数大于200的情况极其少,以至于几乎没有,所以不予考虑。AutoLayout布局规则为在一个父视图中添加若干个子视图,子视图使用随机数在父视图中进行上、下、左、右约束设置,兄弟视图之间不存在约束关系。Frame布局规则为在父视图内随机设置Frame进行布局。当视图个数超过20前,简单布局情况下,AutoLayout布局效率和Frame布局相差不大,但是在超过20后,AutoLayout效率成线性降低趋势。
复杂布局
复杂情况布局如上图所示,其横纵左边和简单布局一致。AutoLayout布局规则为向父视图中添加子视图,同时随机选取其中一个兄弟视图为参照,设置上、下、左、右约束。Frame布局规则同上,但是为了排除无关变量的影响,添加了随机选取兄弟视图的代码。
复杂和简单的对比
上图是复杂布局和简单布局的对比。不难发现,20个视图的时候是一个分水岭。而视图个数在200以内的Frame布局基本稳定在60FPS。但是结合实际开发,界面元素超过50并且进行布局的个数基本没有,同时50个布局的时候FPS稳定在52-54之间,并不会造成明显卡顿,由于测试机器用的是5S,而6,7,8等系列手机性能更强悍,造成的卡顿理论上会更不明显。所以目前来看,AutoLayout的性能还是值得信赖的。

参考

Cassowary算法

WKWebView缓存设计

关于WKWebView

WKWebView是苹果在iOS8之后推出的用于取代UIWebView的一个网页加载框架。它的目的在于解决UIWebView载入速度慢、内存占用大、内存泄漏等问题。

阅读全文

iOS App 开发缓存设计

缓存类型

缓存策略从功能上划分为两种:一、优化型缓存;二、存储型缓存。如果从形式划分,缓存策略又分为内存缓存和磁盘缓存。如下图所示:
缓存划分


阅读全文

面试经历

前言

  • 进入大三下课程少了,但是却变得很忙了。忙着各种找实习,复习课程。人们都说考研苦,考研累。其实某种程度上来说,找实习、找工作的累和苦的程度不比考研低。不仅要复习功课,还要到处留意招聘信息,随时随地保持手机开机,生怕错过一个面试电话,每天无数次翻邮箱希望能看见offer。

  • 文章正文是针对面试的准备,文末有针对iOS开发的干货

阅读全文