iOS 中 Autolayout 优先级的使用

一、约束的优先级

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

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

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

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

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

id="iframe_0.06371598109439769" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/790890-06242068d0f91421.gif?imageMogr2/auto-orient/strip&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.06371598109439769',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 322px; height: 407px;">
 

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

id="iframe_0.37989988571712874" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/790890-1e7ecf2a29755cfb.gif?imageMogr2/auto-orient/strip&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.37989988571712874',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 366px; height: 486px;">
途中因为橙色方块的y值依赖于紫色的方块的位置,然后把紫色方块移除了,所以缺少了Y值的约束,橙色方块然后就开始乱跑了

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

id="iframe_0.8225005244983059" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/790890-385acbb1952a7060.gif?imageMogr2/auto-orient/strip&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.8225005244983059',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 471px; height: 425px;">
 


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

id="iframe_0.46160792693302555" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/790890-c0b69e9cd8413315.gif?imageMogr2/auto-orient/strip&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.46160792693302555',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 366px; height: 465px;">
 


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

 [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的背景色置为灰色。

id="iframe_0.853188832014325" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-0d039c83d9f817c4.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.853188832014325',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 567px; height: 552px;">
 

其运行效果如下:

id="iframe_0.10394974967709869" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-bb45c460258714a8.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.10394974967709869',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 426px;">
 

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

Content Compression Resistance Priority

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

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

id="iframe_0.6853757423628659" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-4d431140ca8ce818.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.6853757423628659',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 567px; height: 552px;">
 

其运行效果如下:

id="iframe_0.5473688774811156" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-ef397b411c030000.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.5473688774811156',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 426px;">
 

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

Content Hugging Priority

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

其运行效果如下:

id="iframe_0.7063715269603268" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-186fa8797b185893.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7063715269603268',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 426px;">
 

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

其运行结果为:

id="iframe_0.7335693615231804" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-ee7325eaa6e29c8b.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7335693615231804',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 426px;">
 

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

实际应用

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

id="iframe_0.10294953950872476" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-9852431bd32694e2.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.10294953950872476',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 377px; height: 136px;">
 

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

id="iframe_0.20348165827780584" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-806c3c778ed7ecf7.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.20348165827780584',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 442px; height: 252px;">
 


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

显示效果如下:

id="iframe_0.8660572480714444" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-32d1606d0d8df24b.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.8660572480714444',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 425px;">
 

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

其运行结果为:

id="iframe_0.7144284450921068" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-c476a6e2c7705ba5.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7144284450921068',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 425px;">
 

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

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

id="iframe_0.09999146968921702" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/628177-307ef20e596bca2b.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.09999146968921702',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 283px; height: 425px;">
 
 
 
怎么使用代码设置优先级
 
用代码设置布局一般都使用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); }];

运行效果

id="iframe_0.1405086213130886" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/938554-90adacfd65abcd4e.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.1405086213130886',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 752px; height: 118px;">
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类型,只要设置右边的比左边的大就可以。

修改后的效果

id="iframe_0.17550472597337285" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/938554-d82ab4580aef0b5d.png?imageMogr2/auto-orient/strip%257CimageView2/2/w/1240&_=5691302%22%20style=%22border:none;max-width:876px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.17550472597337285',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 740px; height: 108px;">
1.png


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

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值