iOS 中 Autolayout 优先级的使用

一、约束的优先级

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];

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利用类似的方法都可以做到优先显示某一个控件的内容。

 

猜你喜欢

转载自blog.csdn.net/Loving_iOS/article/details/53018879
今日推荐