前言
AutoLayout
总结
正文
一. 布局方式与背景
在开始介绍AutoLayout
之前,需要先介绍一下iOS
中的布局方式;iOS中主流的界面布局方式主要有手写代码布局,
xib布局,
storyboard布局;笔者更喜欢的还是手写代码的方式,一个可能与笔者之前
Android经验相关(因为
Android中多是使用手写
xml`的方式布局,另一个是,对于多人合作而言,手写代码其实更方便(减少冲突);当然,对于单人的独立项目,其实没有什么优劣可言,选择一种自己更为熟悉与快捷的方式都是因人而异。
关于xib
布局和storyboard
布局方式的区别,其实二者都是使用IB
来进行可视化空间的拖拽与约束,唯一的区别是二者的侧重点不同;一般来说,单个的xib
文件对应一个ViewController
,对于一些自定义View
,通常也会使用单个xib
并从main bundle
进行加载的方式来载入;而storyboard
只能使用ViewController
而不能用于单独的UIView
(UIView
只能基于ViewController
使用,而xib
同时支持两者)
xib
实际上是一个xml
文件,通过编译之后就得到nib
文件
在上面介绍的三种方式中,都可以使用AutoLayout
的方式来进行布局;AutoLayout
的出现,是为了解决不同尺寸屏幕的适配问题;iPhone 5
之前,屏幕都是3.5
寸的(640 x 960
分辨率),这之前,屏幕尺寸相同,不存在适配问题,所有View
坐标只需要计算好即可,但是2012
年,苹果发布了4.0
寸(640 x 1136
分辨率)的iPhone 5
,这样在iOS
平台上就出现了不同尺寸的移动设备,使得原有的frame
布局方式无法进行很好的屏幕适配,所以为了解决这一问题,就出现了AutoLayout
二. 原理与使用
AutoLayout
其实类似于Android
中的RelativeLayout
,采用View
之间的相对位置来进行布局;我们知道,要确定一个View
的位置,需要知道View
的x,y,width,height
,即View
的起始坐标点,宽度和高度信息;AutoLayout
其实是通过解View
之间建立的线性方程组(y = ax + b
)来确定其信息的(如下图),当然,如果出现约束不完整的情况或者约束冲突的情况,就会出现解的不定性,表现出来即是View
位置未达到预期
可约束的属性参见NSLayoutAttribute枚举
AutoLayout
实际上是基于Cassowary
算法的,Cassowary
是Alan Borning, Kim Marriott, Peter Stuckey
等人在1997
年提出的一个解决布局问题的算法,Cassowary
算法能够有效解决线性等式系统和线性不等式系统,这也是AutoLayout
的性能保障
当使用AutoLayout
的时候,View
的默认初始值会被弃用,如下代码,此时UILable
的初始化宽高会失效;这里还需要注意一个translatesAutoresizingMaskIntoConstraints
属性,从名字我们可以看出,该属性是控制是否把AutoresizingMask
变成约束(autoresizing mask
其实就是完全指定视图的尺寸和位置,即是否需要将其转换为线性方程组);当使用IB(Interface Builder)
布局的时候,即xib,storyboard
方式,如果勾选了Use Autolayout
选项(默认勾选),那么IB
生成的控件的translatesAutoresizingMaskIntoConstraints
属性都会被默认设置false
;当使用手写代码布局时,View
的translatesAutoresizingMaskIntoConstraints
属性默认为true
,但是View
的AutoresizingMask
属性默认被设置为.None
,也就是说如果我们不去动View
的AutoresizingMask
属性,那么AutoresizingMask
就不会对约束产生影响,所以,这个属性,一般也不需要手动设置(当然,为了保险,也可以手动将translatesAutoresizingMaskIntoConstraints
属性置为false
)
当translatesAutoresizingMaskIntoConstraints
属性为true
时,View
的AutoresizingMask
将会转换为约束,一起参与到AutoLayout
的约束计算中,即会对AutoLayout
产生影响
let label = UILabel(frame: CGRect(x: 100, y: 100, width: 100, height: 200))
label.backgroundColor = #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.snp.makeConstraints { (make) in
make.width.equalTo(10)
make.leading.equalToSuperview().offset(40)
make.top.equalToSuperview().offset(40)
}
布局关系不仅限于等于,还可以是大于等于或者小于等于,这种不等关系在处理UILabel
,UIImageView
等具有自身内容尺寸(Intrinsic Content Size
)的控件时非常有用;比如:UILabel
的长度会随文字长度而变化,那么我们可以对UILabel
控件添加两个约束,即宽度大于等于50
与宽度小于等于200
,这样,当文字很少时,宽度也至少为50
,当文字很多时,宽度也不会超过200
某些用来展现内容的用户控件,例如UILabel
、UIButton
、UIImageView
等,它们具有自身内容尺寸(Intrinsic Content Size
),此类用户控件会根据自身内容尺寸添加布局约束;也就是说,如果开发者没有显式给出其宽度或者高度约束,则其自身内容约束将会起作用
具有Intrinsic Content Size
的View
参见下图(摘自官网);具有Intrinsic Content Size
属性的View
都重写了UIView
的-(CGSize)intrinsicContentSize:
方法,并且在需要改变这个值的时候调用invalidateIntrinsicContentSize
方法即可,通知系统这个值改变了;同样,当我们自定义View
的时候,如果想要拥有Intrinsic Content Size
属性,就可以重写该方法
AutoLayout
中还有两个比较重要的概念,Content Hugging
与Content Compression Resistance
约束;在讲解这两个属性之前,需要先了解一下AutoLayout
中的优先级属性;所谓的优先级,我个人的理解其实是一种减少冲突与弱化约束的作用,即为各约束设置优先级,当出现冲突时AutoLayout
优先满足高优先级的约束;关于优先级的使用,可以参见文末参考链接
Content Hugging
约束:不想变大约束;即如果组件的此属性优先级比另一个组件此属性优先级高的话,那么这个组件就保持不变,另一个可以在需要拉伸的时候拉伸;可以简单理解为Content Hugging
越大,View
越难变大;默认值为250
Content Compression Resistance
约束:不想变小约束;如果组件的此属性优先级比另一个组件此属性优先级高的话,那么这个组件就保持不变,另一个可以在需要压缩的时候压缩;可以简单理解为Content Compression Resistance
越大,View
越难变小;默认值为750
关于Content Hugging
与Content Compression Resistance
的应用,可以参见博客
三. 性能分析
-
https://time.geekbang.org/column/article/85332
-
https://www.jianshu.com/p/0b964dc17c04
-
https://xiaozhuanlan.com/topic/5378941206
-
https://draveness.me/layout-performance
-
https://juejin.im/post/5bd5a546f265da0af033cee6#heading-3
四. 参考
- https://www.jianshu.com/p/f6bc007b30e5
- https://blog.csdn.net/hard_man/article/details/50888377