• iOS 自动布局 Autolayout 优先级的使用


    一、约束的优先级

    0.屏幕适配 发展历程

    代码计算frame -> autoreszing(父控件和子控件的关系) -> autolayout(任何控件都可以产生关系) -> sizeclass

    1,简单介绍
    在Autolayout中每个约束都有一个优先级,优先级的范围是1 ~ 1000,默认创建的约束优先级是最高的1000。

    在我理解约束优先级核心就是是为了 "如果存在多套约束的情况下,解决约束冲突" 的问题。有些场景需要动态进行布局,比如我们竖着放了三个按钮:

    如果要求在运行过程中第二个紫色方块有时存在,有时候不存在,如果第二个方块不存在的时候,第一个方块就跑到第二个方块的位置,这个时候如何设置呢?

    2,解决方案
    有一种方案就是,消失后让他启用第二套约束,但是如果对同一个控件,比如设置底部距离紫色方块10和底部距离绿色方块10,这个时候约束就会冲突了,因为约束让控件底部同时满足两个条件,这是不可能同时满足的,就好比,你一让这个人 同时满足 身高1米8 和身高1米6一样, 这时候我们就可以利用调低某个的约束优先级,来让另一个约束优先生效就行。

    布局:
    首先我们先按正常布局下。


     

    然后我们点击删除中间的按钮:


    途中因为橙色方块的y值依赖于紫色的方块的位置,然后把紫色方块移除了,所以缺少了Y值的约束,橙色方块然后就开始乱跑了

    2,设置优先级
    我们只需要给橙色View的底部在加一条距离绿色View顶部的约束并调整优先级低一些就行了,默认都是1000,那么只要比另一个低就行了,比如999(如果添加了这条约束,不调整优先级的话就会报约束冲突。)
    在调整约束优先级后,优先级低的那条线就会变成虚线


     


    4,结果演示
    调整优先级后,默认当然是优先级最高的那个生效,然后我们删除第二个方块后,我们的备用的约束就会生效了,然后就达成我们的目的了。


     


    在代码中我是执行了删除视图的操作。

     [self.v2 removeFromSuperview];

    如果我们想在改变后,做动画,我们只需要,执行layoutIfNeeded代码,比如放到UIView block动画里面,然后动画就起作用了:

     [UIView animateWithDuration:1 delay:0.5 usingSpringWithDamping:0.5 initialSpringVelocity:15 options:UIViewAnimationOptionCurveEaseOut animations:^{
            [self.view layoutIfNeeded];
    } completion:nil];


    注意⚠️:目前我所想到的只有删除视图后,我们的备用的约束才会生效
    removeFromSuperview
    我尝试的拖出来一条距离top 的约束 去更改约束的优先级 self.v2topCons.priority = xxx 发现崩溃了 原因是:
     
    @property UILayoutPriority priority;
    此属性只能作为初始设置的一部分或可选时进行修改。在添加到视图的约束之后,如果优先级从/到NSLayoutPriorityRequired,则会抛出异常。

    3,固有的约束(intrinsic content size

    理论

    固有的约束(intrinsic content size: 有些控件能通过自己显示的内容计算出需要的Size,这个自动计算出来size就叫该控件的固有内容大小。这个大小是和需要显示的内容相关的。UIButton,UILabel就是具有固有内容大小属性的控件。UIButton可以根据它的title字符串长度和需要显示的image来计算需要的Size,UILabel可以根据它的text来计算。

    以下两个约束简单来说就是固有的约束
    Content Hugging Priority: 该优先级表示一个控件抗被拉伸的优先级。优先级越高,越不容易被拉伸,默认是251。
    Content Compression Resistance Priority: 该优先级和上面那个优先级相对应,表示一个控件抗压缩的优先级。优先级越高,越不容易被压缩,默认是750

    有的控件可以根据它自己的内容来计算自身的大小,比如Label 在使用中大家会发现在设置 x y值 后不用设置大小的约束,也不会报错,并且会根据自身内容改变自己打宽度,这都是取决于这两个约束。

    约束优先级的核心就是《哪个约束的优先级高,就使用哪个约束》

    实例

    我们在View中添加了一个UILabel,并为其添加了三个约束:在竖直方向居中,距离左边屏幕145,距离右边屏幕145。为了看到UILabel的实际宽度,我们将Label的背景色置为灰色。


     

    其运行效果如下:


     

    从最后的显示效果来看,中间的Label被压缩了,来满足左右两个约束。
    根据第一个图中标注的优先级,左右约束的优先级比固有内容相关的优先级要高,所以Autolayout布局的时候会优先满足左右两个约束。这时候:左边约束宽度(145) + 右边约束宽度(145) + Label的固有内容宽度 > 屏幕宽度。所以最后只能压缩Label显示的宽度。

    Content Compression Resistance Priority

    这时候Label是被压缩的,我们就来演示一下Content Compression Resistance Priority这个优先级是如何影响控件的抗压缩特性的

    我们修改右边的约束优先级为700


     

    其运行效果如下:


     

    这时候UILabel控件的抗压缩约束优先级比右边约束优先级高,Autolayout先满足左边约束,然后满足UILable控件的固有内容Size的宽度,最后来调整右边约束的宽度。表现出来就是UILable抗压缩特性变强了,它更倾向于显示它固有内容Size。这时候被压缩的就是右边的约束。

    Content Hugging Priority

    为了演示Label被拉伸的情况,我们将右边的约束优先级恢复为1000,并将左右约束的宽度都改为50。

    其运行效果如下:


     

    和压缩的时候类似,左右约束优先级比UILabel的Content Hugging Priority优先级高,并且此时:左边约束宽度(50) + 右边约束宽度(50) + Label的固有内容宽度 < 屏幕宽度。为了满足左右两个约束,就只有拉伸Label。
    此时我们将右边约束的优先级变为240。

    其运行结果为:


     

    这时候UILabel控件的抗拉伸约束优先级比右边约束优先级高,Autolayout先满足左边约束,然后满足UILable控件的固有内容Size的宽度,最后来调整右边约束的宽度。表现出来就是UILable抗拉伸特性变强了,它更倾向于显示它固有内容Size。这时候被拉伸的就是右边的约束。

    实际应用

    我们常常遇到的是类似下面的情况:


     

    TitleLabel的显示内容可能会很长,如果不能很好的设置约束就可能覆盖后面显示时间的Label。但显示时间的Label也应该是一个动态的长度。针对这种情况,我们在模拟器中来模拟一下。


     


    两个Label,前面是AddressLabel,后面是TimeLabel,为了不让两个Label覆盖,我们设定前后Label的水平间距>=10,没有调整过任何优先级。

    显示效果如下:


     

    这时候系统会默认调整前面Label的宽度,使其压缩,来满足后面Label能够完整显示,这应该是系统默认行为。这样看起来刚刚好,但如果我们的需求是时间在前面,并且需要完全显示。地址信息在后面,如果过长就显示省略号。那么使用上面的默认行为就不能满足要求。

    其运行结果为:


     

    这时候前面的TimeLabel会被默认压缩,不能满足要求。这时候我们就可以通过改变控件的抗压缩优先级来满足要求,我们可以把AddressLabel的抗压缩优先级改为740,比TimeLabel默认的750低,那么Autolayout就会去压缩AddressLabel,而使TimeLabel按照它固有内容的宽度显示。这就可以满足我们的要求了。

    来看一下最后的显示效果:

     
     
     
    怎么使用代码设置优先级
     
    用代码设置布局一般都使用masonry,所以我就在使用masonry的基础上写demo用代码设置布局一般都使用masonry,所以我就在使用masonry的基础上写demo

    添加两个label

        UILabel* leftLabel = [[UILabel alloc] init];
        leftLabel.backgroundColor = [UIColor redColor];
        [self.view addSubview:leftLabel];
        leftLabel.text = @"人做的畜生之事越多,内心越是痛苦。";
        [leftLabel sizeToFit];
    
        UILabel* rightLabel = [[UILabel alloc] init];
        rightLabel.backgroundColor = [UIColor greenColor];
        [self.view addSubview:rightLabel];
        rightLabel.text = @"1234567890";
        [rightLabel sizeToFit];

    设置布局

        [leftLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.height.equalTo(@(20));
            make.left.equalTo(self.view).offset(10);
            make.centerY.equalTo(self.view);
            make.right.mas_lessThanOrEqualTo(rightLabel.mas_left);
        }];
    
        [rightLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.height.equalTo(@(20));
            make.left.mas_greaterThanOrEqualTo(leftLabel.mas_right);
            make.right.equalTo(self.view).offset(-10);
            make.centerY.equalTo(leftLabel);
        }];

    运行效果


    0.png

    在默认情况下,我们没有设置各个布局的优先级,那么他就会优先显示左边的label,左边的完全显示后剩余的空间都是右边的label,如果整个空间宽度都不够左边的label的话,那么右边的label没有显示的机会了。

    如果我们现在的需求是优先显示右边的label,左边的label内容超出的省略,这时就需要我们调整约束的优先级了。


    默认情况下两边的label的Content HuggingContent Compression优先级都是一样的,为了让右边的label完全显示,那么我们需要增大右边label的抗压缩级,或者减小左边label的抗压缩级,总之是得让右边的抗压缩级大于左边的label,这样才能让右边的label内容优先显示。

    UIView中关于Content Hugging 和 Content Compression Resistance的方法有:

    - (UILayoutPriority)contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
    - (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
    
    - (UILayoutPriority)contentCompressionResistancePriorityForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
    - (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);

    在初始化label里面添加代码:

    [leftLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];

    或者

    [rightLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];

    UILayoutPriority类型实际上就是float类型,只要设置右边的比左边的大就可以。

    修改后的效果


    1.png


    对于多个labe或者button利用类似的方法都可以做到优先显示某一个控件的内容。

     sizeclass

    * 仅仅是对屏幕进行了分类, 真正排布UI元素还得使用autolayout

    * 不再有横竖屏的概念, 只有屏幕尺寸的概念

    * 不再有具体尺寸的概念, 只有抽象尺寸的概念

    * 把宽度和高度各分为3种情况

    1) Compact : 紧凑(小)

    2) Any : 任意

    3) Regular : 宽松(大)

    4) 符号代表

    - : Compact

    * : Any

    + : Regular

    5) 继承性

    * * : 其它8种情况都会继承

    * - : 会被- - + -继承

    + * : 会被+ - + +继承

    6) sizeclass和autolayout的作用

    sizeclass:仅仅是对屏幕进行了分类

    autolayout:对屏幕中各种元素进行约束(位置尺寸)



  • 相关阅读:
    OOP & Pointer: Segment Tree
    ICPC_2020 上海站
    Notes: Kirchhoff's Matrix 基尔霍夫矩阵
    CS61A Homework: Church Numerals
    题解:[COCI2011-2012#5] BLOKOVI
    题解:SDOI2017 新生舞会
    题解:POI2012 Salaries
    题解:洛谷P1357 花园
    题解:CF593D Happy Tree Party
    题解 P2320 【[HNOI2006]鬼谷子的钱袋】
  • 原文地址:https://www.cnblogs.com/junhuawang/p/5691302.html
Copyright © 2020-2023  润新知