iOS 中 Autolayout 优先级的使用(例如两个label一行显示抗压性设定)

转自:http://www.cnblogs.com/junhuawang/p/5691302.html

一、约束的优先级

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

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:对屏幕中各种元素进行约束(位置\尺寸)



参考资源链接:[苹果官方iOS开发入门指南:核心技术与设计模式解析](https://wenku.csdn.net/doc/31t7gqfwcn?utm_source=wenku_answer2doc_content) 在《苹果官方iOS开发入门指南:核心技术与设计模式解析》,你会找到关于如何使用AutoLayout和MVC设计模式构建用户界面的全面介绍和指导。为了帮助你更好地理解这一过程,我会提供一些关键点和建议。 首先,AutoLayoutiOS开发用来创建动态和响应式用户界面的关键技术。它允许开发者定义界面元素间的相对关系,而不是固定位置,这样无论设备的方向如何变化,界面都能适应屏幕大小和方向的变化。 要开始使用AutoLayout,首先在Xcode的Interface Builder,你可以通过拖拽或使用约束编辑器添加约束来定义界面元素之间的关系。例如,你可能希望一个按钮始终位于屏幕的底部,并且水平居。这时,你可以为按钮添加相应的底部和心对齐的约束。 接下来,了解MVC(Model-View-Controller)模式将帮助你组织代码,并确保界面的逻辑与业务逻辑分开。在MVC模式,Model代表数据和业务逻辑,View是用户界面,而Controller则是Model和View之间的桥梁。 在实现自适应布局时,Controller负责根据不同的屏幕尺寸和方向来更新Model数据,然后将这些更新传递给View。View使用AutoLayout来调整自己的布局,确保界面元素能够正确显示。 例如,如果你有一个包含多个选项的列表视图,你可以在Controller监听屏幕尺寸的变化,然后根据需要调整Model的数据集。View随后会根据新的数据集和AutoLayout约束来更新列表项的显示方式。 总结来说,通过在Xcode使用Interface Builder结合AutoLayout定义界面布局,结合MVC设计模式组织代码逻辑,你就能构建出一个既美观又功能强大的自适应用户界面。《苹果官方iOS开发入门指南:核心技术与设计模式解析》将进一步指导你如何将这些技术应用到实际项目,使你的应用在不同设备上都能提供一致的用户体验。 参考资源链接:[苹果官方iOS开发入门指南:核心技术与设计模式解析](https://wenku.csdn.net/doc/31t7gqfwcn?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值