iOS - AutoLayout

前言

    NS_CLASS_AVAILABLE_IOS(6_0) @interface NSLayoutConstraint : NSObject
    @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 上。

          iOS - AutoLayout_第1张图片

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

          iOS - AutoLayout_第2张图片

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

          iOS - AutoLayout_第3张图片

1、SB/Xib 中 AutoLayout 的设置

  • AutoLayout 使用流程:

    • 启动 AutoLayout 功能。
      • 勾选 Use Auto Layout(默认是开启的)。

        iOS - AutoLayout_第4张图片

    • 摆好控件位置。
    • 添加约束。
    • 给动态的 View 添加 placeholder。
      • 自定义的 View 若要实现 AutoLayout,需要在 Show the Size Inspector 选项卡中设置 Intrinsic Size 为 Placeholder,并实现下面方法。

        iOS - AutoLayout_第5张图片

            - (CGSize)intrinsicContentSize NS_AVAILABLE_IOS(6_0);
    • 更新 constraints 和 frame。
    • 解决有问题的约束。

1.1 添加对齐约束

  • 添加对齐约束

    iOS - AutoLayout_第6张图片

    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

    AutoLayout22

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

1.2 添加相对约束

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

    iOS - AutoLayout_第7张图片

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

    iOS - AutoLayout_第8张图片

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

    iOS - AutoLayout_第9张图片

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

1.3 处理约束

  • 更新、添加、清除约束

    iOS - AutoLayout_第10张图片

    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:

    • 选中添加的约束

      iOS - AutoLayout_第11张图片

    • 修改约束的设置

      iOS - AutoLayout_第12张图片

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

      iOS - AutoLayout_第13张图片

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

      iOS - AutoLayout_第14张图片

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

      iOS - AutoLayout_第15张图片

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

      iOS - AutoLayout_第16张图片

      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 倍
  • 方式 2:

    • 选中设置约束的控件

      iOS - AutoLayout_第17张图片

    • 修改约束的设置

      iOS - AutoLayout_第18张图片

1.5 约束设置警告和错误

  • 警告:

    AutoLayout7

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

    AutoLayout8

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

1.6 约束值设置代码

  • Objective-C

        // 将约束关联到代码中
        @property (weak, nonatomic) IBOutlet NSLayoutConstraint *redViewWidthConstraint;
    
        // 获取约束的值
        /*
            获取到的 constant 值为 CGFloat 型数值
        */
        CGFloat widthConstant = self.redViewWidthConstraint.constant;
    
        // 设置约束的值
        /*
            直接给 constant 赋 CGFloat 型数值
        */
        self.redViewWidthConstraint.constant = 50.0;
  • Swift

        // 将约束关联到代码中
        @IBOutlet weak var redViewWidthConstraint: NSLayoutConstraint!
    
        // 获取约束的值
        /*
            获取到的 constant 值为 CGFloat 型数值
        */
        let widthConstant = self.redViewWidthConstraint.constant
    
        // 设置约束的值
        /*
            直接给 constant 赋 CGFloat 型数值
        */
        self.redViewWidthConstraint.constant = 50.0

1.7 Size Classes 设置

  • 适配不同类型的屏幕

    iOS - AutoLayout_第19张图片

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

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

        iOS - AutoLayout_第20张图片

2、纯代码中 AutoLayout 的设置

  • 代码实现 Autolayout 的步骤:
    • 利用 NSLayoutConstraint 类创建具体的约束对象。
    • 添加约束对象到相应的 view 上。
  • 代码实现 Autolayout 的注意点:
    • 要先禁止 autoresizing 功能,设置要添加约束的控件的下面属性为 NO。

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

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

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

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

          - (void)updateConstraints NS_AVAILABLE_IOS(6_0);
  • VFL 语言:

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

      iOS - AutoLayout_第21张图片

    • VFL 语言格式

      iOS - AutoLayout_第22张图片

    • VFL 语言示例

      iOS - AutoLayout_第23张图片

          // 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

        // 获取当前 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);
    
        // 获取现有约束
    
        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

        // 获取当前 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, *)
    
        // 获取现有约束
    
        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

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

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

2.3 常规语句方式添加约束

  • Objective-C

        + (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;
    
        参数详解:
    
            1、NSLayoutAttribute
    
                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  清除所有约束
    
            2、NSLayoutRelation
    
                NSLayoutRelationLessThanOrEqual = -1,     小于等于
                NSLayoutRelationEqual = 0,                等于
                NSLayoutRelationGreaterThanOrEqual = 1,   大于等于
        // 创建约束
    
        // 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

        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;
    
        参数详解:
    
            1、NSLayoutAttribute
    
                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         清除所有约束
    
            2、NSLayoutRelation
    
                case LessThanOrEqual       小于等于
                case Equal                 等于
                case GreaterThanOrEqual    大于等于
        // 创建约束
    
        // 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

        // 使用 VFL 来创建约束数组
        + (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format 
                                                                        options:(NSLayoutFormatOptions)opts 
                                                                        metrics:(nullable NSDictionary *)metrics 
                                                                          views:(NSDictionary *)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};
        // 约束值
        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

        // 使用 VFL 来创建约束数组
        public class func constraintsWithVisualFormat(format: String, 
                                                options opts: NSLayoutFormatOptions, 
                                                     metrics: [String : AnyObject]?, 
                                                       views: [String : AnyObject]) 
    
                                                            -> [NSLayoutConstraint]
    
            参数:
                format  :VFL 语句
                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]
        // 约束值
        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" 前面。

          // 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" 前面。

          // define this constant if you want to use Masonry without the 'mas_' prefix
          #define MAS_SHORTHAND
    • mas_heightmas_centerX 以此类推。

  • 常用约束的类型:

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

        // 添加新的约束
        [view makeConstraints:^(MASConstraintMaker *make) {
    
        }];
    
        // 删掉以前的所有约束,添加新的约束
        [view remakeConstraints:^(MASConstraintMaker *make) {
    
        }];
    
        // 覆盖以前的某些特定的约束
        [view updateConstraints:^(MASConstraintMaker *make) {
    
        }];
  • 可有可无的用法:

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

          - (MASConstraint *)with {
              return self;
          }
      
          - (MASConstraint *)and {
              return self;
          }

2.5.2 Masonry 的添加

  • Github 网址:https://github.com/SnapKit/Masonry

  • Masonry 使用 ARC

  • Masonry 使用步骤:

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

    • 不需要设置 translatesAutoresizingMaskIntoConstraints 的值。

  • Objective-C

        // 添加第三方库文件
        Masonry
    
        // 添加宏定义
        #define MAS_SHORTHAND
        #define MAS_SHORTHAND_GLOBALS
    
        // 包含头文件
        #import "Masonry.h"

2.5.3 Masonry 基本使用

  • Objective-C

        // 添加控件
    
        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
        }];
  • 运行效果

    iOS - AutoLayout_第24张图片

3、AutoLayout 动画效果设置

  • Objective-C

        // 添加动画效果
        [UIView animateWithDuration:2 animations:^{
    
            // 设置约束值
            /*
                设置约束的语句也可以写在此 block 之外,效果一样
            */
            self.redViewWidthConstraint.constant = 100.0;
    
            // 对 AutoLayout 约束添加动画效果
            /*
                self.view 为添加了约束控件的父控件
            */
            [self.view layoutIfNeeded];
        }];
  • Swift

        // 添加动画效果
        UIView.animateWithDuration(2) { 
    
            // 设置约束值
            /*
                设置约束的语句也可以写在此 block 之外,效果一样
            */
            self.redViewWidthConstraint.constant = 100.0
    
            // 对 AutoLayout 约束添加动画效果
            /*
                self.view 为添加了约束控件的父控件
            */
            self.view.layoutIfNeeded()
        }

4、AutoLayout UILabel 设置效果

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

    iOS - AutoLayout_第25张图片

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

    iOS - AutoLayout_第26张图片

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

    iOS - AutoLayout_第27张图片

    AutoLayout28

你可能感兴趣的:(iOS - AutoLayout)