iOS

AutoLayout总结

iOS布局基础

Posted by 袁平 on March 23, 2019

前言

AutoLayout总结


正文


一. 布局方式与背景

在开始介绍AutoLayout之前,需要先介绍一下iOS中的布局方式;iOS中主流的界面布局方式主要有手写代码布局,xib布局,storyboard布局;笔者更喜欢的还是手写代码的方式,一个可能与笔者之前Android经验相关(因为Android中多是使用手写xml`的方式布局,另一个是,对于多人合作而言,手写代码其实更方便(减少冲突);当然,对于单人的独立项目,其实没有什么优劣可言,选择一种自己更为熟悉与快捷的方式都是因人而异。

关于xib布局和storyboard布局方式的区别,其实二者都是使用IB来进行可视化空间的拖拽与约束,唯一的区别是二者的侧重点不同;一般来说,单个的xib文件对应一个ViewController,对于一些自定义View,通常也会使用单个xib并从main bundle进行加载的方式来载入;而storyboard只能使用ViewController而不能用于单独的UIViewUIView只能基于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的位置,需要知道Viewx,y,width,height,即View的起始坐标点,宽度和高度信息;AutoLayout其实是通过解View之间建立的线性方程组(y = ax + b)来确定其信息的(如下图),当然,如果出现约束不完整的情况或者约束冲突的情况,就会出现解的不定性,表现出来即是View位置未达到预期

可约束的属性参见NSLayoutAttribute枚举

AutoLayout实际上是基于Cassowary算法的,CassowaryAlan 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;当使用手写代码布局时,ViewtranslatesAutoresizingMaskIntoConstraints属性默认为true,但是ViewAutoresizingMask属性默认被设置为.None,也就是说如果我们不去动ViewAutoresizingMask属性,那么AutoresizingMask就不会对约束产生影响,所以,这个属性,一般也不需要手动设置(当然,为了保险,也可以手动将translatesAutoresizingMaskIntoConstraints属性置为false

translatesAutoresizingMaskIntoConstraints属性为true时,ViewAutoresizingMask将会转换为约束,一起参与到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)
}

布局关系不仅限于等于,还可以是大于等于或者小于等于,这种不等关系在处理UILabelUIImageView等具有自身内容尺寸(Intrinsic Content Size)的控件时非常有用;比如:UILabel的长度会随文字长度而变化,那么我们可以对UILabel控件添加两个约束,即宽度大于等于50宽度小于等于200,这样,当文字很少时,宽度也至少为50,当文字很多时,宽度也不会超过200

某些用来展现内容的用户控件,例如UILabelUIButtonUIImageView等,它们具有自身内容尺寸(Intrinsic Content Size),此类用户控件会根据自身内容尺寸添加布局约束;也就是说,如果开发者没有显式给出其宽度或者高度约束,则其自身内容约束将会起作用

具有Intrinsic Content SizeView参见下图(摘自官网);具有Intrinsic Content Size属性的View都重写了UIView-(CGSize)intrinsicContentSize:方法,并且在需要改变这个值的时候调用invalidateIntrinsicContentSize方法即可,通知系统这个值改变了;同样,当我们自定义View的时候,如果想要拥有Intrinsic Content Size属性,就可以重写该方法

AutoLayout中还有两个比较重要的概念,Content HuggingContent Compression Resistance约束;在讲解这两个属性之前,需要先了解一下AutoLayout中的优先级属性;所谓的优先级,我个人的理解其实是一种减少冲突与弱化约束的作用,即为各约束设置优先级,当出现冲突时AutoLayout优先满足高优先级的约束;关于优先级的使用,可以参见文末参考链接

Content Hugging约束:不想变大约束;即如果组件的此属性优先级比另一个组件此属性优先级高的话,那么这个组件就保持不变,另一个可以在需要拉伸的时候拉伸;可以简单理解为Content Hugging越大,View越难变大;默认值为250

Content Compression Resistance约束:不想变小约束;如果组件的此属性优先级比另一个组件此属性优先级高的话,那么这个组件就保持不变,另一个可以在需要压缩的时候压缩;可以简单理解为Content Compression Resistance越大,View越难变小;默认值为750

关于Content HuggingContent Compression Resistance的应用,可以参见博客


三. 性能分析

  1. https://time.geekbang.org/column/article/85332

  2. https://www.jianshu.com/p/0b964dc17c04

  3. https://xiaozhuanlan.com/topic/5378941206

  4. https://draveness.me/layout-performance

  5. https://juejin.im/post/5bd5a546f265da0af033cee6#heading-3


四. 参考

  1. https://www.jianshu.com/p/f6bc007b30e5
  2. https://blog.csdn.net/hard_man/article/details/50888377