zoukankan      html  css  js  c++  java
  • NSLayoutConstraint的使用

    *一切皆代码*
    - --

    #继承关系
    框架|类|类
    :-:|:-:|:-:
    UIKit|NSLayoutConstraint|-
    -|-|-

    #应用场景
    UI界面的搭建一般会占用项目开发相当一部分的时间。涉及到控件布局,控件配置,人机交互,动画效果,数据显示,屏幕适配6个方面,还要考虑视觉效果,性能体验,数据边界(没有数据/很多数据),操作防御(各种狂点)4个方面。另外,UI界面也是开发过程中需求变化比较多的地方。 其中控件布局是最基础和根本的一个方面。

    布局,顾名思义,就是设置控件在屏幕上的位置。自动是相对之前的手动计算控件坐标(frame)布局而言的。NSLayoutConstraint描述某控件与其他控件的位置关系,再由布局引擎在恰当的时候自动计算出该控件的坐标,进而准确的显示到屏幕上。这种位置关系称为约束。当某些变化发生时,布局引擎会根据这些约束自动的调整控件的位置大小。

    这些变化主要来自两个方面:
    外因:屏幕的不同尺寸,屏幕的旋转,父视图的改变,用户行为和视图逻辑
    内因:显示的内容(text,image),字体的调整

    目前,除了原生的NSLayoutConstraint,GitHub上比较主流的自动布局框架还有masonry和SDAutoLayout。masonry相当于原生框架的封装,语法更简洁。SDAutoLayout提供了cell高度自适应等便捷方法。

    # 概念原理
    - 坐标frame
    屏幕采用的是二维坐标系,原点在左上角(0.0),向右x坐标增大,向下y坐标增大。靠近原点偏移时偏移量为负,远离原点偏移时偏移量为正。坐标单位是点point,也是代码中的布局单位。

    - 约束constraint
    一条约束相当于一个线性方程:
    item1.attribute1 [=,<=,>=] multiplier * item2.attribute2 + constant
    一条约束包含5种元素:视图item,属性attribute,关系relationship,乘因子multiplier,常量constant。一个视图有水平垂直两个方向,每个方向需要两个约束确定,即四个约束确定一个视图的位置大小。约束多了,冲突;约束少了,歧义,都会无法正常显示甚至crash。

    -  属性attribute
    描述位置:top,bottom,left,right,centerX,centerY,leading,trailing
    描述尺寸:wight,height
    水平方向:left,right,centerX,wight,leading,trailing
    垂直方向:top,bottom,centerY,height

    - 关系relation
    相当关系 =
    不等关系 >=,<=

    - 乘因子multiplier
    倍数关系

    - 常量constant
    偏移量,注意偏移方向和正负值

    - 优先级priority
    用来描述约束的优先级。数值是1-1000。1000表示约束是必须满足的,其他值表示可选的。约束的优先级默认是必须的(1000)。系统定义了一些字符常量来表示必须,高,低,适应尺寸等优先级,由高到低。不应该显示使用必须优先级,不应该随意指定优先级数值,常用取值999,750,500,250,左右浮动一两个点。尽量使用系统定义的字符常量。布局引擎统筹全局,将首先满足必须的约束。可选约束,能满足就满足,不能满足也会尽量靠近。所以,不能满足的可选约束也会影响最后的显示效果。

    - 固有内容尺寸intrinsic content size
    固有内容尺寸可以理解为默认尺寸。一个控件的内容尺寸由有无显示的内容,内容内边距,可否滑动,有无外在约束决定。核心就是显示的内容。一般能显示内容的控件都有内容尺寸。控件根据内容自适应,父视图根据子视图布局自适应都基于这个概念。具有固有内容尺寸的控件,可以不约束宽或高从而直接使用固有内容尺寸。

    控件|固有内容尺寸
    :-:|:-:
    UIView|没有固有尺寸
    UILabel|宽高都有固有尺寸
    UIButton|宽高都有固有尺寸
    UITextField|宽高都有固有尺寸
    UISwitch|宽高都有固有尺寸,且应该使用固有尺寸(不要约束宽高)
    UIImageView|无内容时没有固有尺寸,有内容后,宽高都有固有尺寸
    UITextView|若能滑动,没有;若不能滑动,默认以将字符串单行显示时的size为固有尺寸;若不能滑动,且约束了宽度,则自动调整高度,来显示全部字符串,以此时的size作为固有尺寸。

    - CHCR约束
    CHCR是一对针对固有尺寸的约束,有固有尺寸的视图水平,垂直两个方向都有一对CHCR约束。使用CHCR需要管理其优先级。
    CH,content hugging,内容抱紧(压缩)
    相当于view.width <= 0.0 * nil.NotAnAttribute + intrinsicwidth
    CR,content compression resistance,内容压缩阻尼(伸展)
    相当于view.width >= 0.0 * nil.NotAnAttribute + intrinsicwidth

    - 固有内容尺寸 intrinsic content size 与 视图size自适应
    当有一个视图supview,使用constraint布局它的subviews。若subviews之间的约束链能明确的确定它们占用的size,就相当于supview的intrinsic content size确定了,系统会自动的计算出intrinsic content size作为supview的size。这时,只需要对supview添加约束,确定其位置就可以了。这种逻辑在水平或垂直某个方向上也是成立的。这就是视图自适应的原理。父视图自适应三个关键点:
    1 . 子视图不要以父视图自适应的那个边为参照。若父视图宽度自适应,就不要以右边为参照,若父视图高度自适应,就不要以底边为参照。
    2 . 子视图的约束链要能明确的确定这些子视图的总的宽(水平方向)或高(水平方向)
    3 . 若父视图是宽度自适应,需要添加一个与最右边的子视图的约束;若父视图是高度自适应,需要添加一个与最下边的子视图的约束(我简称为兜底)


    # 代码流程
    - 布局的具体流程
    1 . 创建控件(alloc,init或new)
    2 . 添加到父视图(addSubview)
    3 . 配置控件属性
    4 . 关闭转换(setTranslatesAutoresizingMaskIntoConstraints)
    5 . 创建约束(一般每个控件4个约束)
    6 . 激活约束(添加约束到最近公共父视图)
    7 . 更新约束/取消激活约束(移除约束)

    - 添加子视图
    ```
    [self.view addSubviews:@[_scroll]];
    ```
    - 关闭转换
    ```
    [_scroll setTranslatesAutoresizingMaskIntoConstraints:NO];
    ```
    - 创建约束
    ```
    NSLayoutConstraint * scroll_top = [NSLayoutConstraint constraintWithItem:_scroll
                                                                       attribute:NSLayoutAttributeTop
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:self.view
                                                                       attribute:NSLayoutAttributeTop
                                                                      multiplier:1.0f
                                                                        constant:0];
    NSLayoutConstraint * scroll_left = [NSLayoutConstraint constraintWithItem:_scroll
                                                                        attribute:NSLayoutAttributeLeft
                                                                        relatedBy:NSLayoutRelationEqual
                                                                           toItem:self.view
                                                                        attribute:NSLayoutAttributeLeft
                                                                       multiplier:1.0f
                                                                         constant:0];
    NSLayoutConstraint * scroll_botton = [NSLayoutConstraint constraintWithItem:_scroll
                                                                          attribute:NSLayoutAttributeBottom
                                                                          relatedBy:NSLayoutRelationEqual
                                                                             toItem:self.view
                                                                          attribute:NSLayoutAttributeBottom
                                                                         multiplier:1.0f
                                                                           constant:0];
    NSLayoutConstraint * scroll_right = [NSLayoutConstraint constraintWithItem:_scroll
                                                                         attribute:NSLayoutAttributeRight
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:self.view
                                                                         attribute:NSLayoutAttributeRight
                                                                        multiplier:1.0f
                                                                          constant:0];

    ```
    特别的:
    ```
    NSLayoutConstraint * top = [NSLayoutConstraint constraintWithItem:view
                                                                       attribute:NSLayoutAttributeWight
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:nil
                                                                       attribute:NSLayoutAttributeNotAnAttribute
                                                                      multiplier:0.0
                                                                        constant:100];
    ```
    - 添加约束
    注意:约束必须添加到所有涉及到的item的最近公共父视图。
    ```
    [self.view addConstraints:@[scroll_top,scroll_left,scroll_botton,scroll_right]];
    ```
    或者(推荐)
    ```
    [NSLayoutConstraint  activateConstraints:@[scroll_top,scroll_left,scroll_botton,scroll_right]];
    ```
    - 移除约束
    ```
    [self.view removeConstraints:@[scroll_top,scroll_left,scroll_botton,scroll_right]];
    ```
    或者(推荐)
    ```
    [NSLayoutConstraint  deactivateConstraints:@[scroll_top,scroll_left,scroll_botton,scroll_right]];
    ```
    - 更新约束
    改变约束的情况:
    改变约束的activate(相当于添加/移除)
    改变约束的constant(主要,直接在需要改变的地方某个已有约束的constant 属性即可)
    改变约束的priority
    移除视图(移除视图会移除与之相关的所有约束)
    - 相关方法
    view的方法
    ```
    [view updateConstraintsIfNeeded];//立即更新view及其子视图的约束
    [view layoutIfNeeded];//立即更新view及其子视图的位置尺寸
    ```
    ```
    [view setNeedsUpdateConstraints];//延时更新约束,系统自动对所有VC调用updateViewConstraints方法,对所有View调用updateConstraints方法;VC 重写updateViewConstraints方法,View重写updateConstraints。例:
    - (void)updateConstraints{
    //这里设置约束
    [super updateConstraints];//必须在后面调用父类该方法。
    }

    [view setNeedsLayout];//延时更新位置尺寸,系统自动对所有VC调用viewWillLayoutSubviews方法,对所有view调用layoutSubviews方法。重写layoutSubviews方法,例:
    - (void)layoutSubviews{
    //这里最后设置坐标frame(不建议)
    [super layoutSubviews];//必须在后面调用父类该方法。
    }
    ```
    ```
    CGSize size = [view systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];//根据约束返回size
    CGSize size = [view systemLayoutSizeFittingSize:UILayoutFittingCompressedSize  withHorizonalFittingPriority:UILayoutPriorityFittingSizeLevelverticalFittingPriority:UILayoutPriorityFittingSizeLevel];//根据约束,水平垂直优先级返回size
    ```
    ```
    [view setContentHuggingPriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];//设置某个方向的CH优先级
    [view setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];//设置某个方向的CR优先级
    ```
    ```
    [view sizeThatFit:currentbounds];//不修改view的size,返回view的size
    [view sizeToFit:currentbounds];//调用sizeThatFit方法,会修改view的size,返回view的size。
    ```
    label的方法
    ```
    label.numberOfLines = 0;//显示不限行数,label高度自适应需要
    label.preferredMaxLayoutWidth = 180;//label高度自适应需要
    ```
    ```
    CGFloat height = label.font.lineHeight;//label的单行高度
    ```
    ```
    CGRect titleRect = [titleLabel.text boundingRectWithSize:CGSizeMake([UIScreen mainScreen].bounds.size.width - 20, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil];//根据text,font等信息返回label的size
    ```
    # 使用原则
    - 创建约束
    1. 先添加到父视图,后添加约束(先添加后约束)
    2. size属性(wight,height)不能对location属性(top,bottom,left,right,centerX,centerY)
    水平属性(left,right,centerX)不能对垂直属性(top,bottom,centerY)。height可对wight。
    3. left/right不能对leading/trailing
    4. 不能只将constant分配给location(top,botton,left,right),centerX,centerY可以。
    5. locaton(top,botton,left,right)中的multiplier必须为1.0。centerX,centerY不必。

    - 添加约束
    1.必须添加到约束涉及到的所有item的最近父视图。若用下面方法添加,系统自动添加的正确的视图。
    ```
    [NSLayoutConstraint  activateConstraints:@[scroll_top,scroll_left,scroll_botton,scroll_right]];
    ```

    - CHCR
    1. 当一列view填充一个空间,若每个view有相同的CH优先级,系统将不知道拉伸哪个viw。此时,应将需要拉伸的view的CH优先级降低。实际,在IB中,每个UILable的CH优先级会自动设为251。
    2. 对于背景不可见的view,如lable,button。它们经常会被莫名其妙的拉伸,导致显示的内容的位置稍有偏差。为防止这种情况,将其CH的优先级升高。
    3. 有些视图,如swith,就应该按照intrinsec content size 显示,将其CH,CR的优先级都升高。
    4. 尽量不要给CHCR优先级设为required(1000)。若想让一个view按照它的intrinsic content size显示,将它的CHCR优先级设为(999)。

    # 代码demo
    - label的宽度自适应
    ```
    ```
    - label的高度自适应
    ```
    ```
    - 父视图size固定的多个label子视图
    - 父视图高度自适应的多个label子视图
    - 父视图宽度自适应的多个label子视图
    - 父视图size自适应的多个label子视图
    - scrollview的contentsize自适应
    - tableviewcell的高度自适应

    # 报错记录
    >错误描述:
    错误码:
    代码环境:
    原因:
    解决方案:

  • 相关阅读:
    c# 泛型(知识整理)
    [VC++]C\C++中结构体知识点强化
    [VC++]CString转化成char
    [VC++]C++中类的多态与虚函数的使用
    [C#]关于自己编写MesasgeBox
    [C#]给DataGridView里的ComboBoxCol添加SelectIndexChange事件
    [C#]用代码触发一个事件
    [C#]序列化例子
    [VC++]怎么使对话框中的按钮DISABLE和ENABLE
    [VC++]控制台程序窗口隐藏
  • 原文地址:https://www.cnblogs.com/gt1989/p/7641935.html
Copyright © 2011-2022 走看看