AutoLayout

自动布局是苹果公司为了适配不同大小屏幕而推出的一种技术方案,旨在实现一次编写布局界面UI,自动适应所有屏幕布局。在这之前多屏幕适配需要跟进不同屏幕分辨率尺寸计算UI控件的坐标,大小。屏幕发生旋转变化后还需要重新计算,开发维护多屏适配需要花费很多人力时间成本,而使用自动布局技术可以非常容易方便的一次性解决这些问题。

自动布局同时还具有支持国际化,根据内容动态调整布局,支持动态字体的独有的优点。

本章简单说明了自动布局的原理,介绍了NSStackView的概念,内容视图中CHCR的概念说明,Xcode对自动布局的支持工具的使用说明,自动布局的多种解决方案的选择原则,常见的自动布局的实例。NSScrollView中如何使用自动布局,通过代码使用自动布局的多种方式;自动布局的常见错误。

AutoLayout原理

传统的布局

在AutoLayout之前传统的布局方式是基于Frame来做UI控件的界面布局的,即设置控件在父视图中的起始坐标点Origin(x,y)和控件的大小Size(width,height)。

FrameLayoutUI

为了适应屏幕变化或者window窗口调整大小,还需要Autoresizing属性设置,确定子控件在父视图中的相对位置关系以及高和宽是否自适应伸缩,关于Autoresizing的不同属性参数请参考View和Window章节中相关的描述。

FrameLayoutUIAutoResize

自动布局

自动布局是一种约束关系,描述的是控件与父视图或与最近相邻的UI元素之间相对位置关系,控件的Frame不需要手工或编程用代码设置而由AutoLayout引擎在视图显示时自动计算。

比如下面的TextView控件4个边在父视图中的相对位置关系为:距顶部top为40像素,底部bottom为120像素,左边为60,右边为60。而根据这4个约束关系,一旦父视图的位置和大小固定,Autolayout在运行时能自动计算出TextView的Frame,即起始坐标和大小。如果用户拉伸视图的window窗口,父视图的大小位置变化后,TextView的Frame又会被重新计算,因此不管窗口如何变化TextView距离父视图4个方向的相对位置距离永远不变。

FrameAutoLayoutUI

自动布局从数学角度看是在UI元素间定义了一组属性的约束关系,形式上是通过等式方程,不等式方程和优先级约束来确定了运行时UI布局的自动调整。

我们把父视图用p表示,子视图用s表示,则上面TextView跟父视图的关系可以表示为

s.top = p.top + 40
p.bottom = s.bottom+ 120
s.left = p.left + 60
p.rigth = s.right + 60
p.frame = (0,0,300,400)

通过上面5个关系条件,就可以唯一确定TextView的frame,实现了UI的自动布局。

自动布局关系的数学定义

更通用的自动布局关系描述定义如下

Item1.Attribute  =  Multiplier * Item2.Attribute + Constant  (其中关系符不限于等号=,还可以为>,<,>=,<=)

其中Item1,Item2代表了UI元素

Attribute为元素的某个属性,所有支付的属性列表后在下面详细说明

Relationship 包括=,>=,<=

Multiplier为比例系数,通常默认为1。如果为0 则表示Item1的某属性直接设置为后面的Constant

Constant为一个常量

自动布局的属性

自动布局中的属性说明如下

AutoLayoutAttributes

优先级约束

为了解决多个约束条件之间的冲突,引出了优先级的概念,即在多个约束条件中优先满足优先级最高的约束条件,最大优先级为1000。

button1.width = 80
button2.width >= button1.width
button2.trailing = parentView.trailing - 20 

Priorities

上面第一个条件规定了button1的宽度为80,第二个条件约束了button2的宽度大于等于button1,第三个条件约束了button2的右边边距跟父视图view为20像素。如果不断拉宽父视图,这3个约束条件同时满足没有任何问题。如果不断缩小父视图的宽度,则第二个约束必然会不能满足。这时候我们说第二个条件和第三个条件发生了冲突,解决的办法是给第二个约束设置比第三个条件更小的优先级。这样就会按第三个条件去计算button2的宽度 而不在要求满足第2条约束条件。

Intrinsic Content Size

对于文本/图片等一些视图控件,可以通过其内在content推算出控件的大小。不是所有的控件都有Intrinsic Content Size。按钮,文本label,文字输入TextField,TextView,ImageView都可以根据内在的content内容计算控件的大小。

基于控件的内容content,有2个特定的约束:content hugging 收缩约束 和 content compression 扩张约束,这2个约束简称为CHCR。

AutoLayoutCHC

IntrinsicHeight代表内部内容的高,IntrinsicWidth代表内部内容的宽

content compression的约束条件:

 View.height >= IntrinsicHeight
 View.width >= IntrinsicWidth

content hugging的约束条件:

 View.height <= IntrinsicHeight
 View.width <= IntrinsicWidth

从这几组约束来看,如果需要完整的显示内容就需要content compression的优先级尽量高,而如果需要尽量显示的紧凑一些占用空间小一些可以将content hugging优先级尽量设置高一些。

NSStackView

StackView是自动布局的优化升级技术,可以把自动布局比作一个大箱子,里面堆放了各种小UI控件的布局设置,而StackView的出现可以想象成小箱子,允许整体的自动布局可以嵌套包括小箱子,每个小箱子可以有自己的局部自动布局设置,小箱子之间可以嵌套。

StackView可以根据布局元素的方向分为水平和垂直布局2种。

StackViewType

Stack View的几个属性:

Orientation 布局方向:Vertical垂直/Horizontal水平

Alignment 对其方式: 布局方向为垂直时为顶部/居中/顶部对齐;水平时为左中右对齐。

Spacing 间距: 布局中每个元素间的空白的间隙

AutoLayout-没有StackView:所有的UI控件都是View的直接子视图,只有一个层级关系。

FrameGenera

AutoLayout-有StackView:View内部布局有了分级关系,整个View包括一个直接的下级Stack View,
Stack View内部分为上中下3个部分:

最上面的几个控件整体在Profile Stack View内,左边是一个ImageView,右边是一个Name Stack View,Name Stack View又分为上下FName Stack View和LName Stack View;

中间部分为一个TextView;

最下面为Button Stack View,里面包括3个按钮控件。

FrameLayout

Xcode中的AutoLayout设置

新建项目中默认是使用autolayout,如果不想使用autolayout而使用传统的布局,可以去掉下图右边面板中autolayout勾选。

XcodeAutolayoutTools

AutoLayout工具菜单介绍

1.Stack

选择界面上的多个控件,点击底部Stack按钮可以将这些控件放置在Stack View里面管理布局。如果需要改变Stack View的方向,从右边属性面板Orientation中选择切换。

如果需要去掉Stack View,从Editor菜单选择Unembed 即可
XcodeAutolayoutStackToo

2.Align

点击Align工具按钮出现Align面板,上面有9种约束关系设置。其中前面7种需要同时至少选择2个控件后才能允许设置,其中最后2个Horizontally in Container和Vertically in Contaoner是约束选中的控件与父视图的关系。每种关系都可以通过右边的输入框输入一个constant常量,如果不输入默认为0。

先选中控件,再点击Align呼出工具面板,在Align面板上选择约束关系,输入constant值,最后点击Align面板底部的Add Constraints按钮完成约束的添加。

添加完成的约束会显示在左边xib文件导航区,点击这个约束可以在右边属性面板看到约束关系。可以对First Iem和Second Item通过下拉框修改属性或者对调交换First 和Second 元素,Relation关系也可以修改。Constant,Priority,Multiplier都可以重新修改设置。

XcodeAutolayoutAlignToo

3.Pin

Pin面板最上部用来设置选择的控件四个边top,bottom,leading,trailing距离父视图或最近的控件之间的间距。
对需要设置的属性点击红色虚线后表示设置此方向的属性。

Width和Heigth用来设置控件的高和宽

Equal Widths: 选中的至少2个控件,约定它们的宽度相同

Equal Heigths:选中的至少2个控件,约定它们的高度相同

Aspect Ratio:选中控件,约定高度和宽度的比例关系。

Align:可以下拉选择不同的属性,类似通过Align面板设置的各种属性,因此这里可以代替大部分前面Align面板中大部分的约束设置的操作。

XcodeAutolayoutPinToo

4.Resolve

分别针对选中的UI控件或所有的UI控件,提供一组菜单功能用于更新Frame,更新约束,自动增加缺少的约束,基于系统建议重置约束,删除约束。
XcodeAutolayoutResolveToo

Update Frame:基于目前的约束,更新UI的Frame,使用这个菜单需要注意如果约束不足或不正确会使UI控件消失或出现在不合适的位置。

Update Constraints:基于目前的位置,更新UI的Constraints,这个菜单常用在在xib中拖动调整了UI的位置以后使用它,这样可以基于新的位置重新生成合适的Contraints。

Clear Constraints:如果觉得约束有问题,可以清除重新设置。

AutoLayout的多种解决方案

问题的多种解决方案

需求:设置TextView的Autolayout使其在父视图SuperView中居中显示。

方案1:设置TextView4个边margin,左右相距50个像素,顶部和底部各121像素。
AutolayoutManySolution1

Scroll View.leading = SuperView.leading + 50
SuperView.trailing  = Scroll View.trailing + 50
Scroll View.top       = SuperView.top + 121
SuperView.bottom = Scroll View.bottom + 121

方案2:设置TextView的左边相距父视图左边50个像素,顶部距父视图顶部121像素,TextView的X方向中心和父视图centerX相同,TextView的Y方向中心和父视图centerY相同。(即水平和垂直方向居中)

AutolayoutManySolution2

Scroll View.leading  = SuperView.leading + 50
Scroll View.top        = SuperView.top + 121
Scroll View.centerX = SuperView.centerX
Scroll View.centerY = SuperView.centerY

方案3:设置TextView的宽度为父视图宽度减去50 * 2个像素,高度为父视图高度减去121 * 2像素,TextView的X方向中心和父视图centerX相同,TextView的Y方向中心和父视图centerY相同。

AutolayoutManySolution3

Scroll View.width    = SuperView.width - 100
Scroll View.height   = SuperView.height - 242
Scroll View.centerX = centerX
Scroll View.centerY = centerY 

可以看出对于一种问题会有多种Autolayout约束解决方案,从前面的约束表达式
Item1.Attribute = Multiplier * Item2.Attribute + Constant来看,这个表达式可以有多种等价的变形形式如:

1.交换item的位置 :Item2.Attribute = Multiplier * Item1.Attribute - Constant

2.乘法因子变为除法:Item2.Attribute = Item1.Attribute / 0.5 - Constant

3.某些属性的等价替换:leading使用left,trailing使用right替换

选择不同的参照物:比如说要使某个元素居中显示,既可以选择父视图为参照,也可以选择另外一个居中的UI元素为参考。

约束选择的原则和建议

使用哪一种约束是最优的方案,每个人的看法也不尽相同。一般的原则和建议如下:

1.对每个UI组件,按人类语言阅读顺序从左到右或从右到左, 即先设置leading,再设置trailing。尽量不要使用left和right属性(这2个属性不支持多语言国际化)。从顶部top到底部bottom顺序设置属性约束。

2.对UI的4个边距设置参考物为最近的元素,没有相邻UI元素参考时使用父视图为参考

3.对Constant使用正整数,即+加上某个数,而不是减去某个数

4.Multiplier因子使用乘法而不是除法。

AutoLayout使用示例

通过各种AutoLayout实例来更好地理解这个技术的使用。

注:省略item而直接使用属性的,默认item为superView,比如top 等价于superView.top

2个View大小相同

AutolayoutTwoSameSizeButton

设置约束如下:

(1) Button.leading = leading + 20
(2) Button.top = top + 20

(3) DynamicButton.leading = Button.trailing + 10
(4) DynamicButton.top = top + 20

(5) DynamicButton.width = Button.width

(6) trailing = DynamicButton.trailing + 20

3个View大小相同

AutolayoutThreeSameSizeButton

(1) Button1.top = top + 27
(2) Button1.leading = leading + 20

(3) Button2.leading = Button1.trailing + 10
(4) Button2.top = top + 27

(5) Button2.width = Button1.width
(6) Button3.width = Button2.width

(7) Button3.top = top + 27
(8) Button3.leading = Button2.trailing + 10

(9) trailing = Button3.trailing + 20

文本标签和输入框

AutolayoutLabel-TextField

(1) First Name.leading = leading + 20
(2) First Name.top = top + 20

(3) Text Field.baseline = First Name.baseline

(4) Text Field.leading = First Name.trailing + 18
(5) Text Field.top = top + 18

(6) trailing = Text Field.trailing + 28

文本标签动态字体和输入框

AutolayoutDynaticLabelTextField

(1) Text Label.leading = leading + 14
(2) Text Label.top = top + 22 @249
(3) Text Label.top ≥ top + 22

(4) Text Label.baseline = Text Field.baseline


(5) Text Field.leading = Text Label.trailing + 8
(6) Text Field.top = top + 20 @249
(7) Text Field.top ≥ top + 20

(8) trailing = Text Field.trailing + 20

在上面第2个约束中设置top=top+22 优先级别为249。第3个约束为Text Label.top ≥ top + 22,默认优先级为1000是最高的。TextLabel可以在运行时修改字体,修改大字体后Label高度变大,此时第2个和第3个约束不能同时满足,优先采用高优先级。因此当字体变大时看到的效果如下:
AutolayoutDynaticLabelTextField2

多行文本标签和输入框

AutolayoutFixedHeightColumns

(1) First Name:.leading = leading + 20
(2) FText Field.leading = First Name:.trailing + 10
(3) FText Field.top = top + 18
(4) trailing = FText Field.trailing + 20
(5) First Name:.baseline = FText Field.baseline


(6) Moiddle Name:.leading = leading + 20
(7) MText Field.leading = Moiddle Name:.trailing + 10
(8) MText Field.top = FText Field.bottom + 10
(9) trailing = MText Field.trailing + 20
(10) Moiddle Name:.baseline = MText Field.baseline


(11) Last Name:.leading = leading + 20
(12) LText Field.leading = Last Name:.trailing + 10
(13) LText Field.top = MText Field.bottom + 10
(14) trailing = LText Field.trailing + 20
(15) Last Name:.baseline = LText Field.baseline


(16) MText Field.width = FText Field.width
(17) LText Field.width = MText Field.width

多行文本标签动态字体和输入框

AutolayoutDynamicHeightColumns

(1) First Label.leading = leading + 20
(2) First Label.top = top + 22 @249
(3) First Label.top ≥ top + 22
(4) FText Field.leading = First Label.trailing + 10
(5) FText Field.top = top + 20 @249
(6) FText Field.top ≥ top + 20
(7) trailing = FText Field.trailing + 20
(8) FText Field.baseline = First Label.baseline

(9) Middle Label.leading = leading + 20
(10) Middle Label.top = First Label.bottom + 14 @249
(11) Middle Label.top ≥ First Label.bottom + 14
(12) MText Field.leading = Middle Label.trailing + 10
(13) MText Field.top = FText Field.bottom + 8 @249
(14) MText Field.top ≥ FText Field.bottom + 8
(15) MText Field.width = FText Field.width
(16) trailing = MText Field.trailing + 20
(17) MText Field.baseline = Middle Label.baseline

(18) Last Label.leading = leading + 20
(19) Last Label.top = Middle Label.bottom + 14 @249
(20) Last Label.top ≥ Middle Label.bottom + 14
(21) LText Field.leading = Last Label.trailing + 10
(22) LText Field.top ≥ MText Field.bottom + 10
(23) LText Field.top = MText Field.bottom + 10 @249
(24) LText Field.width = MText Field.width
(25) trailing = LText Field.trailing + 20
(26) LText Field.baseline = Last Label.baseline

修改Label字体后的效果,3个文本输入框宽度保持一致
AutolayoutDynamicHeightColumns2

2个View大小相同,水平方向的间距相同

思路:在2个Button View之间填充3个Custom View(NSView)做为辅助,3个Custom View宽度相等,高度为0
AutoLayoutEqualSpacing

(1) Custom View1.leading = leading
(2) Custom View1.top = top + 19
(3) Custom View1.heigth = 0

(4) Button1.leading = Custom View1.trailing + 2
(5) Button1.top = top + 20

(6) Custom View2.leading = Button1.trailing + 2
(7) Custom View2.top = top + 20
(8) Custom View2.width = Custom View1.width
(9) Custom View2.heigth = 0

(10) Button2.leading = Custom View2.trailing + 2
(11) Button2.top = top + 20
(12) Button2.width = Button1.width

(13) Custom View3.leading = Button2.trailing + 2
(14) Custom View3.top = top + 19
(15) Custom View3.width = Custom View2.width
(16) Custom View3.heigth = 0

(17) trailing = Custom View3.trailing

运行后的效果
AutoLayoutEqualSpacing2

简单的Stack View

AutoLayoutSimpleStack

(1) Stack View.leading = leading + 1
(2) trailing = Stack View.trailing - 1
(3) Stack View.top = top
(4) bottom = Stack View.bottom

复杂界面未使用Stack View

AutoLayoutNoStackVie

(1) ImageView.leading = leading + 30
(2) ImageView.top = top + 28
(3) ImageView.width = 62
(4) ImageView.height = 62

(5) First Name.leading = ImageView.trailing + 8
(6) FText Field.leading = First Name.trailing + 8
(7) FText Field.baseline = First Name.baseline
(8) FText Field.top = top + 28
(9) trailing = Text Field.trailing + 30

(10) Last Name.leading = ImageView.trailing + 9
(11) LText Field.leading = Last Name.trailing + 8
(12) LText Field.baseline = Last Name.baseline
(13) LText Field.top = Text Field.bottom + 8
(14) trailing = LText Field.trailing + 30

(15) Scroll View.leading = leading + 20
(16) trailing = Scroll View.trailing + 20
(17) Scroll View.top = ImageView.bottom + 40

(18) Button1.leading = leading + 41
(19) Button1.top = Scroll View.bottom + 40
(20) bottom = Button1.bottom + 34

(21) Button2.leading = Button1.trailing + 8
(22) Button2.top = Scroll View.bottom + 40
(23) bottom = Button2.bottom + 34
(24) Button2.width = Button1.width

(25) Button3.leading = Button2.trailing + 8
(26) Button3.top = Scroll View.bottom + 40
(27) bottom = Button3.bottom + 34
(28) trailing = Button3.trailing + 41
(29) Button3.width = Button2.width

复杂界面使用多个嵌套Stack View

AutoLayoutUseStackVie

(1) Stack View.leading = leading + 20
(2) trailing = Stack View.trailing + 20
(3) Stack View.top = top + 81
(4) bottom = Stack View.bottom + 65
(5) imageView.width = 62

可以看出同样的UI布局使用NSStack View可以大幅度减少约束的设置,因此在复杂布局中推荐优先使用NSStack。

NSScrollView使用AutoLayout

新建xib文件,拖放一个ScrollView控件到视图,设置其居中,使用Pin工具设置它的4个边距约束如图示:
AutolayoutScrollVie

Scroll View.leading = leading + 53
Scroll View.top = top + 66
trailing = Scroll View.trailing + 53
bottom = Scroll View.bottom + 66

设置ScrollView的内容View的约束,距离顶部和左边都是0,添加约束后xib文件导航树右边出现了红色告警,暂时忽略等添加完ImageView 后自动会消失
AutolayoutScrollViewVie

View.top = top
View.leading = leading

从控件工具箱拖一个ImageView到ScrollView的内容视图View上,设置ImageView约束距离4个边都是0,点击Add 4 Constraints完成,这时候红色告警消失
AutolayoutScrollViewImageVie

Image View.top = top
bottom = Image View.bottom
Image View.leading = leading
trailing = Image View.trailing

选取一张比较大的图片添加到工程,给imageView设置image属性为新加的图片名。运行工程发现图片可以在水平和垂直2个方向拖动。

注意:上面每个约束属性前面没有item的默认表示item为Super View,即bottom表示Super View.bottom。

总结一下NSScrollView的使用xib设置约束的步骤:

1.设置NSScrollView 4个方向的边距

2.设置NSScrollView的contentView 的 top和leading 与父视图距离都是0

3.拖放需要的UI控件到NSScrollView的contentView上,设置其距离父视图4个方向的边距都是0

通过代码设置AutoLayout

Cocoa有多种方式通过代码来添加约束

使用NSLayoutConstraint类

NSLayoutConstraint类中属性,分别对应了约束关系表达式中的不同的部分

@property NSLayoutPriority priority;
@property (readonly, assign) id firstItem;
@property (readonly) NSLayoutAttribute firstAttribute;
@property (readonly) NSLayoutRelation relation;
@property (nullable, readonly, assign) id secondItem;
@property (readonly) NSLayoutAttribute secondAttribute;
@property (readonly) CGFloat multiplier;
@property CGFloat constant;

使用代码构造的NSLayoutConstraint实例等价与前面的关系表达式

Item1.Attribute = Multiplier * Item2.Attribute + Constant

+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

下面是一个例子,新建一个currentView实例,构造其与父视图4个边的距离都是0

- (void)addSubView {
    
    NSView *currentView = [[NSView alloc]init];
    [self.contentView addSubview:currentView];
    [self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
    
    NSLayoutConstraint *viewTop = [NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
   
    NSLayoutConstraint *viewBottom = [NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
    
    NSLayoutConstraint *viewLeading = [NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0];
    
    NSLayoutConstraint *viewTrailing = [NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0];
    
    [self.view addConstraint:viewTop];
    [self.view addConstraint:viewBottom];
    [self.view addConstraint:viewLeading];
    [self.view addConstraint:viewTrailing];
}

使用NSLayoutConstraint类来添加约束的步骤:

1.首先将子视图使用addSubview添加到父视图

2.设置父视图的setTranslatesAutoresizingMaskIntoConstraints为NO,否则系统会根据frame自动创建一组constraints,可能会跟代码创建的产生冲突

3.使用NSLayoutConstraint的constraintWithItem方法构造每个关系表达式

4.使用addConstraint添加约束

高版本系统使用NSLayoutAnchor

NSView类中扩展了约束的属性,每个属性继承NSLayoutAnchor类,可以直接使用下面的属性设置关系((仅限于OSX系统>=10.11,iOS>=9.0)

@interface NSView (NSConstraintBasedLayoutInstallingConstraints)

@property (readonly, strong) NSLayoutXAxisAnchor *leadingAnchor ;
@property (readonly, strong) NSLayoutXAxisAnchor *trailingAnchor;
@property (readonly, strong) NSLayoutXAxisAnchor *leftAnchor ;
@property (readonly, strong) NSLayoutXAxisAnchor *rightAnchor ;
@property (readonly, strong) NSLayoutYAxisAnchor *topAnchor;
@property (readonly, strong) NSLayoutYAxisAnchor *bottomAnchor ;
@property (readonly, strong) NSLayoutDimension *widthAnchor ;
@property (readonly, strong) NSLayoutDimension *heightAnchor;
@property (readonly, strong) NSLayoutXAxisAnchor *centerXAnchor ;
@property (readonly, strong) NSLayoutYAxisAnchor *centerYAnchor;
@property (readonly, strong) NSLayoutYAxisAnchor *firstBaselineAnchor ;
@property (readonly, strong) NSLayoutYAxisAnchor *lastBaselineAnchor ;

NSLayoutAnchor间的操作方法,等于,大于等于,小于等于

@interface NSLayoutAnchor<AnchorType> : NSObject

// These methods return an inactive constraint of the form thisAnchor = otherAnchor.
-(NSLayoutConstraint*)constraintEqualToAnchor:(NSLayoutAnchor*)anchor;
-(NSLayoutConstraint*)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor*)anchor;
-(NSLayoutConstraint*)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor*)anchor;

// These methods return an inactive constraint of the form thisAnchor = otherAnchor + constant.
-(NSLayoutConstraint*)constraintEqualToAnchor:(NSLayoutAnchor*)anchor constant:(CGFloat)c;
-(NSLayoutConstraint*)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor*)anchor constant:(CGFloat)c;
-(NSLayoutConstraint*)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor*)anchor constant:(CGFloat)c;
@end

myView.top = otherView.top + 10
上述关系表达式使用View的NSLayoutAnchor属性表示如下:
[myView.topAnchor constraintEqualToAnchor:otherView.topAnchor constant:10];

使用VFL定义约束关系

VFL
详细的定义及其使用说明请参考苹果官方指南

使用第三方库Masonry

Masonry对AutoLayout操作做了非常方便的封装

下面是view1设置在superview 4个方向的代码,非常简单明了。

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top);
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}]

AutoLayout常见错误

1.设计阶段的问题:约束条件不足或者发生冲突,Autolayout引擎无法通过约束计算出UI控件的frame时。在xib设计时很容易发现,通常会有红色告警出现,可以根据提示解决。

2.运行时错误:具有相同的优先级或潜在的冲突导致运行时无法正确的计算,一般程序会crash会抛出异常信息,需要根据异常信息分析定位

3.逻辑错误:没有任何错误,但是UI布局显示位置或大小不是期望的,可以重新分析约束的设置而修改。