AutoLayout 自动布局

前言

  • Objective-C

    1
    NS_CLASS_AVAILABLE_IOS(6_0) @interface NSLayoutConstraint : NSObject
  • Swift

    1
    @available(iOS 6.0, *) public class NSLayoutConstraint : NSObject
  • 1)Autolayout

    • 在 Autolayout 之前,有 Autoresizing 可以作屏幕适配,但局限性较大,有些任务根本无法完成(只能解决子控件跟父控件的相对关系问题,不能解决兄弟控件的相对关系问题)。相比之下,Autolayout 的功能比 Autoresizing 强大很多。
    • Auto Layout 是苹果在 iOS 6 (Xcode 4) 中新引入的布局方式,旨在解决 3.5 寸和 4 寸屏幕的适配问题,由于 Xcode 4 的不给力,当时并没有得到很大推广,自 iOS 7(Xcode 5)开始,Auto Layout 的开发效率得到很大的提升。屏幕适配工作在 iPhone 6 及 plus 发布以后变得更加重要,苹果官方也推荐开发者尽量使用 Autolayout 来布局 UI 界面,Autolayout 能很轻松地解决屏幕适配的问题。

    • Autolayout 的 2 个核心概念:

      • 参照
      • 约束
  • 2)约束

    • Auto Layout 的本质是依靠某几项约束条件来达到对某一个元素的定位。我们可以在某个地方只使用一个约束,以达到一个小目的,例如防止内容遮盖、防止边界溢出等。但实践证明,如果把页面上每一个元素的位置都用 Auto Layout 进行 “严格约束” 的话,那么 Auto Layout 可以帮我们省去非常多的计算 frame 的代码。

    • “严格约束” 是什么?简单来说,严格约束就是对某一个元素的绝对定位,让它在任一屏幕尺寸下都有着唯一的位置。这里的绝对定位不是定死的位置,而是对一个元素完善的约束条件。我们要在一个直角坐标系里描述一个矩形。那么只需要指定这个矩形的位置和大小。那么只要给出上图中的四个值即可:到左边界的距离,到上边界的距离,宽度,高度。这四个约束是最简单的情况。在对一个元素进行严格约束时,请直接在脑中构建这个元素,并且加上几条约束条件,如果他无法缩放和动弹,那么严格约束就是成功的!必须牢记,使用 Auto Layout 时最重要的是:对页面上每一个元素都进行严格约束,不严格的约束是万恶之源。

    • 约束主要分为以下几种:

      • 相对于父 view 的约束。如:距离上边距 10,左边距 10。
      • 相对于前一个元素的约束。如:距离上一个元素 20,距离左边的元素 5 等。
      • 对齐类约束。如:跟父 view 左对齐,跟上一个元素居中对齐等。
      • 相等约束。如:跟父 view 等宽。
    • 约束特点:

      • Constraint 可以跨层级

        • 层级中不能有⾃己实现 layoutsubViews 的 View;
        • 不能连接有可变边界的 View,如 ScrollView。
      • Constraint 不会替换前一个。

      • 控件知道⾃己内容⼤⼩。

    • 约束添加的规则:

      • 在创建约束之后,需要将其添加到作用的 view 上,在添加时要注意目标 view 需要遵循以下规则:

        • 相对于父视图的约束,添加到父视图上;
        • 相对于另一个控件的约束,添加到其共有的父视图上;
        • 跨层级的约束,添加到其最上层的父视图上;
        • 自身的宽高等约束,添加到自身视图上。

        • 1)对于两个同层级 view 之间的约束关系,添加到它们的父 view 上。

          AutoLayout9

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

          AutoLayout10

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

          AutoLayout11

1、SB/Xib 中 AutoLayout 的设置

  • AutoLayout 使用流程:

    • 启动 AutoLayout 功能。

      • 勾选 Use Auto Layout(默认是开启的)。

        AutoLayout16

    • 摆好控件位置。

    • 添加约束。
    • 给动态的 View 添加 placeholder。

      • 自定义的 View 若要实现 AutoLayout,需要在 Show the Size Inspector 选项卡中设置 Intrinsic Size 为 Placeholder,并实现下面方法。

        AutoLayout15

        1
        - (CGSize)intrinsicContentSize NS_AVAILABLE_IOS(6_0);
    • 更新 constraints 和 frame。

    • 解决有问题的约束。

1.1 添加对齐约束

  • 添加对齐约束

1.1.1 Add New Alignment Constraints

AutoLayout1

Leading Edges 左边缘对齐
Trailing Edges 右边缘对齐
Top Edges 上边缘对齐
Bottom Edges 下边缘对齐
Horizontal Centers 水平中心对齐
Vertical Centers 垂直中心对齐
Baselines 文本底标线对齐,大多数控件中等同于 Bottom Edges
Horizontal Center in Container 在父控件中水平居中
Vertical Center in Container 在父控件中垂直居中
Update Frames 根据约束条件更新 Frame

1.1.2 Update Frames

AutoLayout22

None 不更新
Items of New Constraints 新添加约束的元素
All Frames in Container 容器内的所有 Frame

1.2 添加相对约束

  • 添加相邻、自身、相等约束

1.2.1 Add New Constraints

AutoLayout2

Spacing to nearest neighbor 与相邻的控件间的距离
Constrain to margins 边缘约束(用于防边缘触摸,选中自动缩进 20)
Width 控件的宽度
Height 控件的高度
Equal Widths 相等的宽度
Equal Heights 相等的高度
Aspect Ratio 相等的长宽比
Align 对齐方式
Update Frames 根据约束条件更新 Frame

1.2.2 Top Constraints

AutoLayout20

Use Standard Value 使用标准值
Use Current Canvas Value 使用当前值
View 视图控件
Top Layout Guide 状态栏下边缘处

1.2.3 Bottom Constraints

AutoLayout21

Use Standard Value 使用标准值
Use Current Canvas Value 使用当前值
Buttom Layout Guide 屏幕下边缘处
View 视图控件

1.3 处理约束

  • 更新、添加、清除约束

1.3.1 Update Constraints

AutoLayout3

Selected Views 选中的控件
– Update Frames 更新 Frame
– Update Constraints 更新约束
– Add Missing Constraints 添加缺失的约束
– Reset to Suggested Constraints 添加建议的(自动)约束
– Clear Constraints 清除约束
All Views in View Controller 所有控件
– Update Frames 更新 Frame
– Update Constraints 更新约束
– Add Missing Constraints 添加缺失的约束
– Reset to Suggested Constraints 添加建议的(自动)约束
– Clear Constraints 清除约束

1.4 修改约束

  • 更改添加的约束

1.4.1 方式 1

  • 选中添加的约束

    AutoLayout17

  • 修改约束的设置

AutoLayout4

First Item 第一个控件的约束值,要设置的控件
Relation 第一个控件与第二个控件约束值之间的关系
– Second Item 第二个控件的约束值,参照的控件
– Constraint 约束值增加量
– Priority 约束优先级
– Multiplier 约束值放大倍数
– Placeholder
—- Remove at build time 编译时移除该约束
– Installed 添加该约束

AutoLayout23

Leading Margin 左边距
Center X Within Margins 父控件水平中心
Trailing Margin 右边距
Relative to margin 相对于边缘
respect language direction 遵循本地语言方向
Reverse First And Second Item 调换 First 和 Second 两个控件的设置位置

AutoLayout24

Less Than or Equal 小于等于
Equal 等于
Greater Than or Equal 大于等于

AutoLayout25

Required(1000) 默认优先级(高优先级)
High(750) 中优先级
Low(250) 低优先级

AutoLayout26

Reverse Multiplier 反转倍数(即 0.5 变为 2,4:3 变为 3:4)
Convert to Decimal 转换为十进制
Presets 预设值(也可以不使用预设值,自己设置需要的倍数,如 0.5)
– 1 1 倍
– 4:3 4:3 倍
– 16:9 16:9 倍

1.4.2 方式 2

  • 选中设置约束的控件

    AutoLayout18

  • 修改约束的设置

    AutoLayout19

1.5 约束设置警告和错误

  • 警告:

    AutoLayout7

    • 控件的 frame 不匹配所添加的约束。比如约束控件的宽度为 100, 而控件现在的宽度是 110。
  • 错误:

    AutoLayout8

    • 缺乏必要的约束。比如只约束了宽度和高度, 没有约束具体的位置。
    • 两个约束冲突。比如一个约束控件的宽度为 100,一个约束控件的宽度为 110。

1.6 约束值设置代码

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 将约束关联到代码中
    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *redViewWidthConstraint;

    // 获取约束的值
    /*
    获取到的 constant 值为 CGFloat 型数值
    */
    CGFloat widthConstant = self.redViewWidthConstraint.constant;

    // 设置约束的值
    /*
    直接给 constant 赋 CGFloat 型数值
    */
    self.redViewWidthConstraint.constant = 50.0;
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 将约束关联到代码中
    @IBOutlet weak var redViewWidthConstraint: NSLayoutConstraint!

    // 获取约束的值
    /*
    获取到的 constant 值为 CGFloat 型数值
    */
    let widthConstant = self.redViewWidthConstraint.constant

    // 设置约束的值
    /*
    直接给 constant 赋 CGFloat 型数值
    */
    self.redViewWidthConstraint.constant = 50.0

1.7 Size Classes 设置

  • 适配不同类型的屏幕

    AutoLayout5

    • 在同一个文件中设置不同类型的屏幕尺寸适配。
    • 选择不同尺寸的宽度和高度的组合,可以设置在不同类型屏幕上加载不同的控件或约束。

    • 启动 Size Classes 功能。

      • 勾选 Use Size Classes(默认是开启的)。

        AutoLayout16

2、纯代码中 AutoLayout 的设置

  • 代码实现 Autolayout 的步骤:

    • 利用 NSLayoutConstraint 类创建具体的约束对象。
    • 添加约束对象到相应的 view 上。
  • 代码实现 Autolayout 的注意点:

    • 要先禁止 autoresizing 功能,设置要添加约束的控件的下面属性为 NO。

      1
      redView.translatesAutoresizingMaskIntoConstraints = NO;
    • 添加约束之前,一定要保证相关控件都已经在各自的父控件上。

      1
      [self.view addSubview:redView];
    • 不用再给 view 设置 frame。

    • 如果是 View Controller,则 AutoLayout 适配写在下面方法中。

      1
      - (void)updateViewConstraints NS_AVAILABLE_IOS(6_0);
    • 如果是 View,则 AutoLayout 适配写在下面方法中。

      1
      - (void)updateConstraints NS_AVAILABLE_IOS(6_0);

2.1 VFL 语言

  • VFL 全称是 Visual Format Language,翻译过来是 “可视化格式语言”,VFL 是苹果公司为了简化 Autolayout 的编码而推出的抽象语言。

    AutoLayout12

  • VFL 语言格式

表达式 功能 说明
H: 水平方向 默认方向
V: 垂直方向
[view] Views 要添加约束的控件
( ) 约束值 控件自身的约束,如宽高等
\ 父控件 如屏幕边框等
>=,==,<= 关系
- 空间,间隙 中间没有值时表示默认为 8,不写时表示为 0
@value 优先级 默认为 1000
  • VFL 语言示例

    AutoLayout6

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // canelButton 宽 72,acceptButton 宽 50,它们之间间距 12。
    H:[cancelButton(72)]-12-[acceptButton(50)]

    // wideView 宽度大于等于 60point,该约束条件优先级为 700(优先级最大值为 1000,优先级越高的约束越先被满足)。
    H:[wideView(>=60@700)]

    // 竖直方向上,先有一个 redBox,其下方紧接一个高度等于 redBox 高度的 yellowBox。
    V:[redBox][yellowBox(==redBox)]

    // 水平方向上,Find 距离父 view 左边缘默认间隔宽度,之后是 FindNext 距离 Find 间隔默认宽度;
    // 再之后是宽度不小于 20 的 FindField,它和 FindNext 以及父 view 右边缘的间距都是默认宽度。
    // 竖线 “|” 表示 superview 的边缘。
    H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|

2.1 约束设置方法

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 获取当前 view 中所有的约束
    - (NSArray *)constraints NS_AVAILABLE_IOS(6_0);

    // 将指定的约束添加到页面,相对于另一个视图的约束必须添加到其父视图上
    - (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
    - (void)addConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);

    // 将指定的约束从页面中移除
    - (void)removeConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
    - (void)removeConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);

    // 激活或者停用指定约束
    @property (getter=isActive) BOOL active NS_AVAILABLE(10_10, 8_0);

    // 激活指定约束
    + (void)activateConstraints:(NSArray *)constraints NS_AVAILABLE(10_10, 8_0);

    // 停用指定约束
    + (void)deactivateConstraints:(NSArray *)constraints NS_AVAILABLE(10_10, 8_0);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 获取现有约束

    NSArray *constraints = [self.view constraints];

    // 添加约束

    [self.view addConstraint:constraintX];
    [self.view addConstraint:constraintY];
    [redView addConstraint:constraintW];
    [redView addConstraint:constraintH];

    [self.view addConstraints:@[constraintX, constraintY, constraintW, constraintH]];

    // 激活指定约束

    constraintX.active = YES;
    constraintY.active = YES;
    constraintW.active = YES;
    constraintH.active = YES;

    [NSLayoutConstraint activateConstraints:@[constraintX, constraintY, constraintW, constraintH]];

    // 删除约束

    [self.view removeConstraint:constraintX];
    [self.view removeConstraint:constraintY];

    [self.view removeConstraints:@[constraintX, constraintY]];

    // 停用指定约束

    constraintX.active = NO;
    constraintY.active = NO;

    [NSLayoutConstraint deactivateConstraints:@[constraintX, constraintY]];
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 获取当前 view 中所有的约束
    public var constraints: [NSLayoutConstraint] { get } @available(iOS 6.0, *)

    // 将指定的约束添加到页面,相对于另一个视图的约束必须添加到其父视图上
    public func addConstraint(constraint: NSLayoutConstraint) @available(iOS 6.0, *)
    public func addConstraints(constraints: [NSLayoutConstraint]) @available(iOS 6.0, *)

    // 将指定的约束从页面中移除
    public func removeConstraint(constraint: NSLayoutConstraint) @available(iOS 6.0, *)
    public func removeConstraints(constraints: [NSLayoutConstraint]) @available(iOS 6.0, *)

    // 激活或者停用指定约束
    public var active: Bool @available(iOS 8.0, *)

    // 激活指定约束
    public class func activateConstraints(constraints: [NSLayoutConstraint]) @available(iOS 8.0, *)

    // 停用指定约束
    public class func deactivateConstraints(constraints: [NSLayoutConstraint]) @available(iOS 8.0, *)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 获取现有约束

    let constraints = self.view.constraints()

    // 添加约束

    self.view.addConstraint(constraintX)
    self.view.addConstraint(constraintY)
    redView.addConstraint(constraintW)
    redView.addConstraint(constraintH)

    self.view.addConstraints([constraintX, constraintY, constraintW, constraintH])

    // 激活指定约束

    constraintX.active = true
    constraintY.active = true
    constraintW.active = true
    constraintH.active = true

    NSLayoutConstraint.activateConstraints([constraintX, constraintY, constraintW, constraintH])

    // 删除约束

    self.view.removeConstraint(constraintX)
    self.view.removeConstraint(constraintY)

    self.view.removeConstraints([constraintX, constraintY])

    // 停用指定约束

    constraintX.active = false
    constraintY.active = false

    NSLayoutConstraint.deactivateConstraints([constraintX, constraintY])

2.2 关闭 Autoresizing

  • Objective-C

    1
    2
    3
    4
    5
    6
    // 纯代码添加约束必须先关闭 Autoresizing
    /*
    不要将 AutoresizingMask 转为 Autolayout 的约束
    每个添加约束的控件都需要设置
    */
    redView.translatesAutoresizingMaskIntoConstraints = NO;
  • Swift

    1
    2
    3
    4
    5
    6
    // 纯代码添加约束必须先关闭 Autoresizing
    /*
    不要将 AutoresizingMask 转为 Autolayout 的约束
    每个添加约束的控件都需要设置
    */
    redView.translatesAutoresizingMaskIntoConstraints = false

2.3 常规语句方式添加约束

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    + (instancetype)constraintWithItem:(id)view1 
    attribute:(NSLayoutAttribute)attr1
    relatedBy:(NSLayoutRelation)relation
    toItem:(nullable id)view2
    attribute:(NSLayoutAttribute)attr2
    multiplier:(CGFloat)multiplier
    constant:(CGFloat)c;

    参数说明:
    第一个参数 view1: 要约束的控件;
    第二个参数 attr1: 约束的类型(做怎样的约束);
    第三个参数 relation: 与参照控件之间的关系;
    第四个参数 view2: 参照的控件;
    第五个参数 attr2: 约束的类型(做怎样的约束);
    第六个参数 multiplier: 乘数,控件 1 的指定属性是参照控件 2 指定属性的多少倍;
    第七个参数 c: 常量,控件 1 的指定属性需要加的浮点数。

    根据参数的讲解,得出计算公式如下:

    view1.attr1 [= , >= , <=] view2.attr2 * multiplier + c;

    参数详解:

    1NSLayoutAttribute

    NSLayoutAttributeLeft = 1, 左边缘,CGRectGetMinX(view.frame)
    NSLayoutAttributeRight, 右边缘,CGRectGetMaxX(view.frame)
    NSLayoutAttributeTop, 上边缘,CGRectGetMinY(view.frame)
    NSLayoutAttributeBottom, 下边缘,CGRectGetMinY(view.frame)
    NSLayoutAttributeLeading, 前边缘,在习惯由左向右看的地区相当于 Left,
    在习惯从右至左看的地区相当于 Right
    NSLayoutAttributeTrailing, 后边缘,在习惯由左向右看的地区相当于 Right,
    在习惯从右至左看的地区相当于 Left
    NSLayoutAttributeWidth, 宽度,CGRectGetWidth(view.frame)
    NSLayoutAttributeHeight, 高度,CGRectGetHeight(view.frame)
    NSLayoutAttributeCenterX, 水平中心,view.center.x
    NSLayoutAttributeCenterY, 垂直中心,view.center.y
    NSLayoutAttributeBaseline, 文本底标线
    NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline, 文本底标线
    NSLayoutAttributeFirstBaseline 文本上标线,NS_ENUM_AVAILABLE_IOS(8_0)

    NSLayoutAttributeLeftMargin 左边缘,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeRightMargin 右边缘,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeTopMargin 上边缘,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeBottomMargin 下边缘,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeLeadingMargin 前边缘,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeTrailingMargin 后边缘,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeCenterXWithinMargins 宽度,NS_ENUM_AVAILABLE_IOS(8_0)
    NSLayoutAttributeCenterYWithinMargins 高度,NS_ENUM_AVAILABLE_IOS(8_0)

    NSLayoutAttributeNotAnAttribute = 0 清除所有约束

    2NSLayoutRelation

    NSLayoutRelationLessThanOrEqual = -1, 小于等于
    NSLayoutRelationEqual = 0, 等于
    NSLayoutRelationGreaterThanOrEqual = 1, 大于等于
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // 创建约束

    // redView 的左边缘与 greenView 的左边缘对齐
    NSLayoutConstraint *constraintX = [NSLayoutConstraint constraintWithItem:redView
    attribute:NSLayoutAttributeLeft
    relatedBy:NSLayoutRelationEqual
    toItem:greenView
    attribute:NSLayoutAttributeLeft
    multiplier:1.0
    constant:0];

    // redView 的上边缘等于 greenView 的上边缘加 100
    NSLayoutConstraint *constraintY = [NSLayoutConstraint constraintWithItem:redView
    attribute:NSLayoutAttributeTop
    relatedBy:NSLayoutRelationEqual
    toItem:greenView
    attribute:NSLayoutAttributeTop
    multiplier:1.0
    constant:100];

    // redView 的宽度等于 100
    NSLayoutConstraint *constraintW = [NSLayoutConstraint constraintWithItem:redView
    attribute:NSLayoutAttributeWidth
    relatedBy:NSLayoutRelationEqual
    toItem:nil
    attribute:NSLayoutAttributeNotAnAttribute
    multiplier:1.0
    constant:100];

    // redView 的高度等于 50
    NSLayoutConstraint *constraintH = [NSLayoutConstraint constraintWithItem:redView
    attribute:NSLayoutAttributeHeight
    relatedBy:NSLayoutRelationEqual
    toItem:nil
    attribute:NSLayoutAttributeNotAnAttribute
    multiplier:1.0
    constant:50];

    // 将约束添加到视图上
    /*
    相对于另一个视图的约束必须添加到其父视图上
    */
    [self.view addConstraint:constraintX];
    [self.view addConstraint:constraintY];
    [redView addConstraint:constraintW];
    [redView addConstraint:constraintH];
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    public convenience init(item view1: AnyObject, 
    attribute attr1: NSLayoutAttribute,
    relatedBy relation: NSLayoutRelation,
    toItem view2: AnyObject?,
    attribute attr2: NSLayoutAttribute,
    multiplier: CGFloat,
    constant c: CGFloat)

    参数说明:

    第一个参数 view1: 要设置的视图;
    第二个参数 attr1: view1 要设置的属性;
    第三个参数 relation: 视图 view1 和 view2 的指定属性之间的关系;
    第四个参数 view2: 参照的视图;
    第五个参数 attr2: 参照视图 view2 的属性;
    第六个参数 multiplier: 视图 view1 的指定属性是参照视图 view2 指定属性的多少倍;
    第七个参数 c: 视图 view1 的指定属性需要加的浮点数。

    根据参数的讲解,得出计算公式如下:

    view1.attr1 [= , >= , <=] view2.attr2 * multiplier + c;

    参数详解:

    1NSLayoutAttribute

    case Left 左边缘,CGRectGetMinX(view.frame)
    case Right 右边缘,CGRectGetMaxX(view.frame)
    case Top 上边缘,CGRectGetMinY(view.frame)
    case Bottom 下边缘,CGRectGetMinY(view.frame)
    case Leading 前边缘,在习惯由左向右看的地区相当于 Left,在习惯从右至左看的地区相当于 Right
    case Trailing 后边缘,在习惯由左向右看的地区相当于 Right,在习惯从右至左看的地区相当于 Left
    case Width 宽度,CGRectGetWidth(view.frame)
    case Height 高度,CGRectGetHeight(view.frame)
    case CenterX 水平中心,view.center.x
    case CenterY 垂直中心,view.center.y
    case Baseline 文本底标线

    case FirstBaseline 文本上标线,@availability(iOS, introduced=8.0)

    case LeftMargin 左边缘,@availability(iOS, introduced=8.0)
    case RightMargin 右边缘,@availability(iOS, introduced=8.0)
    case TopMargin 上边缘,@availability(iOS, introduced=8.0)
    case BottomMargin 下边缘,@availability(iOS, introduced=8.0)
    case LeadingMargin 前边缘,@availability(iOS, introduced=8.0)
    case TrailingMargin 后边缘,@availability(iOS, introduced=8.0)
    case CenterXWithinMargins 宽度,@availability(iOS, introduced=8.0)
    case CenterYWithinMargins 高度,@availability(iOS, introduced=8.0)

    case NotAnAttribute 清除所有约束

    2NSLayoutRelation

    case LessThanOrEqual 小于等于
    case Equal 等于
    case GreaterThanOrEqual 大于等于
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // 创建约束

    // redView 的左边缘与 greenView 的左边缘对齐
    let constraintX = NSLayoutConstraint(item: redView,
    attribute: .Left,
    relatedBy: .Equal,
    toItem: greenView,
    attribute: .Left,
    multiplier: 1.0,
    constant: 0)

    // redView 的上边缘等于 greenView 的上边缘加 100
    let constraintY = NSLayoutConstraint(item: redView,
    attribute: .Top,
    relatedBy: .Equal,
    toItem: greenView,
    attribute: .Top,
    multiplier: 1.0,
    constant: 100)

    // redView 的宽度等于 100
    let constraintW = NSLayoutConstraint(item: redView,
    attribute: .Width,
    relatedBy: .Equal,
    toItem: nil,
    attribute: .NotAnAttribute,
    multiplier: 1.0,
    constant: 100)

    // redView 的高度等于 50
    let constraintH = NSLayoutConstraint(item: redView,
    attribute: .Height,
    relatedBy: .Equal,
    toItem: nil,
    attribute: .NotAnAttribute,
    multiplier: 1.0,
    constant: 50)

    // 将约束添加到视图上
    /*
    相对于另一个视图的约束必须添加到其父视图上
    */
    self.view.addConstraint(constraintX)
    self.view.addConstraint(constraintY)
    redView.addConstraint(constraintW)
    redView.addConstraint(constraintH)

2.4 VFL 语句方式添加约束

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // 使用 VFL 来创建约束数组
    + (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
    options:(NSLayoutFormatOptions)opts
    metrics:(nullable NSDictionary<NSString *,id> *)metrics
    views:(NSDictionary<NSString *, id> *)views;

    参数: format :VFL 语句 opts :约束类型 metrics :约束值 views :需要设置约束的控件

    NSLayoutFormatOptions 约束类型:

    NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft), 左边缘对齐
    NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight), 右边缘对齐
    NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop), 上边缘对齐
    NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom), 下边缘对齐
    NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading), 前边缘对齐
    NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing), 后边缘对齐
    NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX), 水平中心对齐
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY), 垂直中心对齐
    NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline), 文本底标线对齐
    NSLayoutFormatAlignAllLastBaseline = NSLayoutFormatAlignAllBaseline, 文本底标线对齐
    NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline),
    文本上标线对齐
    NSLayoutFormatAlignmentMask = 0xFFFF, 无对齐

    /* choose only one of these three
    */
    NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default 由前到后方向,默认
    NSLayoutFormatDirectionLeftToRight = 1 << 16, 由左到右方向
    NSLayoutFormatDirectionRightToLeft = 2 << 16, 由右到左方向

    NSLayoutFormatDirectionMask = 0x3 << 16, 无方向

    // 使用下面的宏来自动生成 views 和 metrics 参数
    NSDictionaryOfVariableBindings(...)

    NSDictionaryOfVariableBindings(redView); 等价于 @{@"redView": redView};
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // 约束值
    NSNumber *margin = @50;

    NSNumber *width = @100;
    NSNumber *height = @50;

    // 创建水平方向约束
    /*
    redView 的宽度为 100
    kNilOptions = 0,相当于 NSLayoutFormatAlignmentMask
    */

    NSDictionary *viewsH = NSDictionaryOfVariableBindings(redView);
    NSDictionary *metricsH = NSDictionaryOfVariableBindings(width);

    NSString *vflH = @"H:[redView(width)]";

    NSArray *constraintH = [NSLayoutConstraint constraintsWithVisualFormat:vflH
    options:kNilOptions
    metrics:metricsH
    views:viewsH];

    // 创建垂直方向约束
    /*
    redView 的上边缘等于 greenView 的上边缘加 100(greenView 的高度为 50)
    redView 与 greenView 的左边缘对齐
    redView 的高度等于 50
    */

    NSDictionary *viewsV = NSDictionaryOfVariableBindings(redView, greenView);
    NSDictionary *metricsV = NSDictionaryOfVariableBindings(margin, height);

    NSString *vflV = @"V:[greenView]-margin-[redView(height)]";

    NSArray *constraintV = [NSLayoutConstraint constraintsWithVisualFormat:vflV
    options:NSLayoutFormatAlignAllLeft
    metrics:metricsV
    views:viewsV];
    // 将约束添加到视图上
    [self.view addConstraints:constraintH];
    [self.view addConstraints:constraintV];
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    // 使用 VFL 来创建约束数组
    public class func constraintsWithVisualFormat(format: String,
    options opts: NSLayoutFormatOptions,
    metrics: [String : AnyObject]?,
    views: [String : AnyObject])

    -> [NSLayoutConstraint]

    参数: formatVFL 语句 opts :约束类型 metrics :约束值 views :需要设置约束的控件

    NSLayoutFormatOptions 约束类型:

    public static var AlignAllLeft: NSLayoutFormatOptions { get } 左边缘对齐
    public static var AlignAllRight: NSLayoutFormatOptions { get } 右边缘对齐
    public static var AlignAllTop: NSLayoutFormatOptions { get } 上边缘对齐
    public static var AlignAllBottom: NSLayoutFormatOptions { get } 下边缘对齐
    public static var AlignAllLeading: NSLayoutFormatOptions { get } 前边缘对齐
    public static var AlignAllTrailing: NSLayoutFormatOptions { get } 后边缘对齐
    public static var AlignAllCenterX: NSLayoutFormatOptions { get } 水平中心对齐
    public static var AlignAllCenterY: NSLayoutFormatOptions { get } 垂直中心对齐
    public static var AlignAllBaseline: NSLayoutFormatOptions { get } 文本底标线对齐
    public static var AlignAllLastBaseline: NSLayoutFormatOptions { get } 文本底标线对齐
    @available(iOS 8.0, *)
    public static var AlignAllFirstBaseline: NSLayoutFormatOptions { get } 文本上标线对齐

    public static var AlignmentMask: NSLayoutFormatOptions { get } 无对齐

    /* choose only one of these three
    */
    public static var DirectionLeadingToTrailing: NSLayoutFormatOptions { get } 由前到后方向,默认
    public static var DirectionLeftToRight: NSLayoutFormatOptions { get } 由左到右方向
    public static var DirectionRightToLeft: NSLayoutFormatOptions { get } 由右到左方向

    public static var DirectionMask: NSLayoutFormatOptions { get } 无方向

    // 生成 views 和 metrics 参数
    ["redView":redView]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // 约束值
    let margin: NSNumber = 50

    let width: NSNumber = 100
    let height: NSNumber = 50

    // 创建水平方向约束
    /*
    redView 的宽度为 100
    kNilOptions = 0
    */

    let viewsH = ["redView":redView]
    let metricsH = ["width":width]

    let vflH = "H:[redView(width)]"

    let constraintH = NSLayoutConstraint.constraintsWithVisualFormat( vflH,
    options: .AlignmentMask,
    metrics: metricsH,
    views: viewsH)

    // 创建垂直方向约束
    /*
    redView 的上边缘等于 greenView 的上边缘加 100(greenView 的高度为 50)
    redView 与 greenView 的左边缘对齐
    redView 的高度等于 50
    */

    let viewsV = ["redView":redView, "greenView":greenView]
    let metricsV = ["margin":margin, "height":height]

    let vflV = "V:[greenView]-margin-[redView(height)]"

    let constraintV = NSLayoutConstraint.constraintsWithVisualFormat( vflV,
    options: .AlignAllLeft,
    metrics: metricsV,
    views: viewsV)

    // 将约束添加到视图上
    self.view.addConstraints(constraintH)
    self.view.addConstraints(constraintV)

2.5 Masonry 框架方式添加约束

2.5.1 Masonry 简介

  • Masonry 是目前最流行的 Autolayout 第三方框架,用优雅的代码方式编写 Autolayout,省去了苹果官方复杂的 Autolayout 代码,大大提高了开发效率。

  • mas_equalTo 和 equalTo:

    • 默认情况下 mas_equalTo 有自动包装功能,比如自动将 20 包装为 @20,equalTo 没有自动包装功能。如果添加了下面的宏,那么 mas_equalToequalTo 就没有区别,这个宏一定要添加到 #import "Masonry.h" 前面。

      1
      2
      // define this constant if you want to enable auto-boxing for default syntax
      #define MAS_SHORTHAND_GLOBALS
  • mas_width 和 width:

    • 默认情况下 width 是 make 对象的一个属性,用来添加宽度约束用的,表示对宽度进行约束。mas_width 是一个属性值,用来当做 equalTo 的参数,表示某个控件的宽度属性。如果添加了下面的宏,mas_width 也可以写成 width,就不用带 mas_ 前缀,这个宏一定要添加到 #import "Masonry.h" 前面。

    • mas_heightmas_centerX 以此类推。

      1
      2
      // define this constant if you want to use Masonry without the 'mas_' prefix
      #define MAS_SHORTHAND
  • 常用约束的类型:

    1
    2
    3
    4
    尺寸  :width\height\size
    边界 :left\leading\right\trailing\top\bottom
    中心点:center\centerX\centerY
    边界 :edges
  • 添加约束的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 添加新的约束
    [view makeConstraints:^(MASConstraintMaker *make) {

    }];

    // 删掉以前的所有约束,添加新的约束
    [view remakeConstraints:^(MASConstraintMaker *make) {

    }];

    // 覆盖以前的某些特定的约束
    [view updateConstraints:^(MASConstraintMaker *make) {

    }];
  • 可有可无的用法:

    • 以下方法都仅仅是为了提高可读性,可有可无。

      1
      2
      3
      4
      5
      6
      7
      - (MASConstraint *)with {
      return self;
      }

      - (MASConstraint *)and {
      return self;
      }

2.5.2 Masonry 的添加

  • Github 网址:Masonry

  • Masonry 使用 ARC

  • Masonry 使用步骤:

    • 添加 Masonry 文件夹的所有源代码到项目中。
    • 添加 2 个宏、添加主头文件,主头文件一定要放在宏定义的后面。

    • 不需要设置 translatesAutoresizingMaskIntoConstraints 的值。

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 添加第三方库文件
    Masonry

    // 添加宏定义
    #define MAS_SHORTHAND
    #define MAS_SHORTHAND_GLOBALS

    // 包含头文件
    #import "Masonry.h"

2.5.3 Masonry 基本使用

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    // 添加控件

    UIView *greenView = UIView.new;
    greenView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:greenView];

    UIView *redView = UIView.new;
    redView.backgroundColor = UIColor.redColor;
    [self.view addSubview:redView];

    UIView *blueView = UIView.new;
    blueView.backgroundColor = UIColor.blueColor;
    [self.view addSubview:blueView];

    UIView *superview = self.view;

    // 约束值

    int padding = 10;

    // 添加约束

    [greenView makeConstraints:^(MASConstraintMaker *make) {

    make.top.greaterThanOrEqualTo(superview.top).offset(padding);
    make.left.equalTo(superview.left).offset(padding);
    make.bottom.equalTo(blueView.top).offset(-padding);
    make.right.equalTo(redView.left).offset(-padding);

    make.width.equalTo(redView.width);

    make.height.equalTo(redView.height);
    make.height.equalTo(blueView.height);
    }];

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {

    make.top.equalTo(superview.mas_top).with.offset(padding); // with with
    make.left.equalTo(greenView.mas_right).offset(padding); // without with
    make.bottom.equalTo(blueView.mas_top).offset(-padding);
    make.right.equalTo(superview.mas_right).offset(-padding);

    make.width.equalTo(greenView.mas_width);

    make.height.equalTo(@[greenView, blueView]); // can pass array of views
    }];

    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {

    make.top.equalTo(greenView.mas_bottom).offset(padding);
    make.left.equalTo(superview.mas_left).offset(padding);
    make.bottom.equalTo(superview.mas_bottom).offset(-padding);
    make.right.equalTo(superview.mas_right).offset(-padding);

    make.height.equalTo(@[greenView.mas_height, redView.mas_height]); // can pass array of attributes
    }];
  • 运行效果

    AutoLayout30

3、AutoLayout 动画效果设置

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 添加动画效果
    [UIView animateWithDuration:2 animations:^{

    // 设置约束值
    /*
    设置约束的语句也可以写在此 block 之外,效果一样
    */
    self.redViewWidthConstraint.constant = 100.0;

    // 对 AutoLayout 约束添加动画效果
    /*
    self.view 为添加了约束控件的父控件
    */
    [self.view layoutIfNeeded];
    }];
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 添加动画效果
    UIView.animateWithDuration(2) {

    // 设置约束值
    /*
    设置约束的语句也可以写在此 block 之外,效果一样
    */
    self.redViewWidthConstraint.constant = 100.0

    // 对 AutoLayout 约束添加动画效果
    /*
    self.view 为添加了约束控件的父控件
    */
    self.view.layoutIfNeeded()
    }

4、AutoLayout UILabel 设置效果

  • 在没有设置 Autolayout 时,UILabel 的文字内容总是居中显示,导致顶部和底部会有一大片空缺区域。

    AutoLayout13

  • 设置 Autolayout(位置约束和宽度约束)后,UILabel 的 bounds 默认会自动包住所有的文字内容,顶部和底部不再会有空缺区域。

    AutoLayout14

  • 设置宽度约束为小于等于某个值,Label 的文字较短时,Label 的宽度也会随着缩小。

    AutoLayout27

    AutoLayout28

文章目录
  1. 1. 前言
  2. 2. 1、SB/Xib 中 AutoLayout 的设置
    1. 2.1. 1.1 添加对齐约束
      1. 2.1.1. 1.1.1 Add New Alignment Constraints
      2. 2.1.2. 1.1.2 Update Frames
    2. 2.2. 1.2 添加相对约束
      1. 2.2.1. 1.2.1 Add New Constraints
      2. 2.2.2. 1.2.2 Top Constraints
      3. 2.2.3. 1.2.3 Bottom Constraints
    3. 2.3. 1.3 处理约束
      1. 2.3.1. 1.3.1 Update Constraints
    4. 2.4. 1.4 修改约束
      1. 2.4.1. 1.4.1 方式 1
      2. 2.4.2. 1.4.2 方式 2
    5. 2.5. 1.5 约束设置警告和错误
    6. 2.6. 1.6 约束值设置代码
    7. 2.7. 1.7 Size Classes 设置
  3. 3. 2、纯代码中 AutoLayout 的设置
    1. 3.1. 2.1 VFL 语言
    2. 3.2. 2.1 约束设置方法
    3. 3.3. 2.2 关闭 Autoresizing
    4. 3.4. 2.3 常规语句方式添加约束
    5. 3.5. 2.4 VFL 语句方式添加约束
    6. 3.6. 2.5 Masonry 框架方式添加约束
      1. 3.6.1. 2.5.1 Masonry 简介
      2. 3.6.2. 2.5.2 Masonry 的添加
      3. 3.6.3. 2.5.3 Masonry 基本使用
  4. 4. 3、AutoLayout 动画效果设置
  5. 5. 4、AutoLayout UILabel 设置效果
隐藏目录