zoukankan      html  css  js  c++  java
  • 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:对屏幕中各种元素进行约束(位置尺寸)



  • 相关阅读:
    LeetCode 123. Best Time to Buy and Sell Stock III (stock problem)
    精帖转载(关于stock problem)
    LeetCode 122. Best Time to Buy and Sell Stock II (stock problem)
    LeetCode 121. Best Time to Buy and Sell Stock (stock problem)
    LeetCode 120. Triangle
    基于docker 搭建Elasticsearch5.6.4 分布式集群
    从零开始构建一个centos+jdk7+tomcat7的docker镜像文件
    Harbor实现容器镜像仓库的管理和运维
    docker中制作自己的JDK+tomcat镜像
    docker镜像制作---jdk7+tomcat7基础镜像
  • 原文地址:https://www.cnblogs.com/junhuawang/p/5691302.html
Copyright © 2011-2022 走看看