iOS知识---针对多个显示内容空间不定内容长度的显示问题Content Hugging 和 Content Compression Resistance

来源:互联网 发布:如何制作淘宝店铺模板 编辑:程序博客网 时间:2024/06/06 04:02

       在开发中有的时候回遇到一些比较操蛋的数据长度不定,但是需要用多个lable在同一行显示的问题,有的内容必须要显示全,有些则在到达一定长度之后剩余内容允许以...的形式省略显示。针对这样的问题需要借助Autolayout来完成。为了便利操作可以采用Masonry来进行约束操作。

Content Hugging 和 Content Compression Resistance

这两个属性对有intrinsic content size的控件(例如button,label)非常重要。通俗的讲,具有intrinsic content size的控件自己知道(可以计算)自己的大小,例如一个label,当你设置text,font之后,其大小是可以计算到的。关于intrinsic content size官方的解释:

Custom views typically have content that they display of which the layout system is unaware. Overriding this method allows a custom view to communicate to the layout system what size it would like to be based on its content. This intrinsic size must be independent of the content frame, because there’s no way to dynamically communicate a changed width to the layout system based on a changed height, for example.

好了,了解了intrinsic content size的概念之后,下面就重点讨论Content Hugging 和 Content Compression Resistance了。

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

- (UILayoutPriority)contentCompressionResistancePriorityForAxis:(UILayoutConstraintAxis)axis

- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis

- (UILayoutPriority)contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis

- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis


大概的意思就是设置优先级的。

Hugging priority 确定view有多大的优先级阻止自己变大。

Compression Resistance priority确定有多大的优先级阻止自己变小。


 并排两个label,宽度由内容决定。父级View宽度不够时,优先显示左边label的内容

遇到这种跟内容压缩、优先级有关的布局,就不得不提Autolayout中的两个重要的属性“Content Compression Resistance”和“Content Hugging”。

Content Compression Resistance = 不许挤我!

对,这个属性说白了就是“不许挤我”=。=

这个属性的优先级(Priority)越高,越不“容易”被压缩。也就是说,当整体的空间装不小所有的View的时候,Content Compression Resistance优先级越高的,现实的内容越完整。

Content Hugging = 抱紧!

这个属性的优先级越高,整个View就要越“抱紧”View里面的内容。也就是View的大小不会随着父级View的扩大而扩大。

分析

根据要求,可以将约束分为两个部分:

  1. 整体空间足够时,两个label的宽度由内容决定,也就是说,label的“Content Hugging”优先级很高,而且没有固定的Width属性。

  2. 整体空间不够时,左边的label更不容易被压缩,也就是“Content Compression Resistance”优先级更高。

重点:

  1. label不设置具体的宽度(width)属性,宽度由内容决定。

  2. 显示的优先级由“Content Compression Resistance”属性的高低决定。

约束示例图

2.jpg

关键代码

关键的代码如下:(label1是左边的label,label2是右边的)

设置位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// label1: 位于左上角

[_label1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(_contentView1.mas_top).with.offset(5);
    make.left.equalTo(_contentView1.mas_left).with.offset(2);
    // 40高度
    make.height.equalTo(@40);
}];

// label2: 位于右上角

[_label2 mas_makeConstraints:^(MASConstraintMaker *make) {

    //左边贴着label1,间隔2
    make.left.equalTo(_label1.mas_right).with.offset(2);
    //上边贴着父view,间隔5
    make.top.equalTo(_contentView1.mas_top).with.offset(5);
    //右边的间隔保持大于等于2,注意是lessThanOrEqual
    //这里的“lessThanOrEqualTo”放在从左往右的X轴上考虑会更好理解。
    //即:label2的右边界的X坐标值“小于等于”containView的右边界的X坐标值。
    make.right.lessThanOrEqualTo(_contentView1.mas_right).with.offset(-2);
    //只设置高度40
    make.height.equalTo(@40);
}];

设置内容约束

1
2
3
4
5
6
7
8
9
10
11
12
//设置label1的content hugging 为1000

[_label1 setContentHuggingPriority:UILayoutPriorityRequired
                           forAxis:UILayoutConstraintAxisHorizontal];
//设置label1的content compression 为1000

[_label1 setContentCompressionResistancePriority:UILayoutPriorityRequired
                                         forAxis:UILayoutConstraintAxisHorizontal];
//设置右边的label2的content hugging 为1000

[_label2 setContentHuggingPriority:UILayoutPriorityRequired
                           forAxis:UILayoutConstraintAxisHorizontal];
//设置右边的label2的content compression 为250

[_label2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                         forAxis:UILayoutConstraintAxisHorizontal];

小节

灵活运用“Content Compression Resistance”和“Content Hugging”属性。


 四个ImageView整体居中,可以任意显示、隐藏

先看看示例的截图:

blob.png

下面的四个Switch控件分别控制上面对应位置的图片是否显示。

分析

  1. 首先就是整体居中,为了实现这个,最简单的办法就是将四个图片“装进”一个容器View里面,然后让这个容器View在整个页面中居中即可。这样就不用控制每个图片的居中效果了。

  2. 然后就是显示与隐藏。在这里我直接控制图片ImageView的宽度,宽度为0的时候不就“隐藏”了吗。

约束示例图

4.jpg

解释

之所以这么设置,主要目的有以下几点:

  1. 尽量减少无效的约束,保证约束不多也不少。

  2. 内部的每个imageView约束其实都只有四个:left、centerY、width和height,这样有个好处,就是可以写个函数,专门为View一次性添加这几个约束,大大减少代码量。

  3. 最右边的imageView还要单独设置跟容器View的右边约束,是为了不用设置容器View的width,保证容器View是刚好包含内部的View的,这样整体才是居中的。

关键代码

先看看设置每个imageView约束的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
*  设置view的宽高、左边约束,垂直中心约束
*
*  @param view    要设置的view
*  @param size    CGSize
*  @param left    左边对齐的约束
*  @param centerY 垂直中心对齐的约束
*
*  @return 返回宽约束,用于显示、隐藏单个view
*/
- (MASConstraint *)setView:(UIView *)view size:(CGSize)size left:(MASViewAttribute *)left centerY:(MASViewAttribute *)centerY {
    __block MASConstraint *widthConstraint;
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        //宽高固定
        widthConstraint = make.width.equalTo(@(size.width));
        make.height.equalTo(@(size.height));
        //左边约束
        make.left.equalTo(left);
        //垂直中心对齐
        make.centerY.equalTo(centerY);
    }];
    return widthConstraint;
}

接着就是设置容器View的代码:

1
2
3
4
5
6
7
8
9
//containerView 就是 容器View
[_containerView mas_makeConstraints:^(MASConstraintMaker *make) {
    //只设置高度,宽度由子View决定
    make.height.equalTo(@(IMAGE_SIZE));
    //水平居中
    make.centerX.equalTo(self.view.mas_centerX);
    //距离父View顶部200点
    make.top.equalTo(self.view.mas_top).offset(200);
}];

最后是内部imageView的约束:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//分别设置每个imageView的宽高、左边、垂直中心约束,注意约束的对象

//每个View的左边约束和左边的View的右边相等=。=,有点绕口...

UIImageView *imageView1 = _imageViews[0];

MASConstraint *width = [self setView:imageView1 size:imageViewSize left:_containerView.mas_left centerY:_containerView.mas_centerY];

[_widthConstraints addObject:width];

UIImageView *imageView2 = _imageViews[1];

width = [self setView:imageView2 size:imageViewSize left:imageView1.mas_right centerY:_containerView.mas_centerY];

[_widthConstraints addObject:width];

UIImageView *imageView3 = _imageViews[2];

width = [self setView:imageView3 size:imageViewSize left:imageView2.mas_right centerY:_containerView.mas_centerY];

[_widthConstraints addObject:width];

UIImageView *imageView4 = _imageViews[3];

width = [self setView:imageView4 size:image

eViewSize left:imageView3.mas_right centerY:_containerView.mas_centerY];

[_widthConstraints addObject:width];

//最后设置最右边的imageView的右边与父view的最有对齐

[imageView4 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.right.equalTo(_containerView.mas_right);
}];

控制ImageView显示、隐藏的时候,直接让其宽度等于0就行:

1
2
3
4
5
6
7
8
9
- (IBAction)showOrHideImage:(UISwitch *)sender {
    NSUInteger index = (NSUInteger) sender.tag;
    MASConstraint *width = _widthConstraints[index];
    if (sender.on) {
        width.equalTo(@(IMAGE_SIZE));
    else {
        width.equalTo(@0);
    }
}

小节

有时候用个“容器View”管理内部的View,往往会起到事半功倍的效果。而且在组织约束的时候,尽量的将约束统一起来,这样可以用一个函数去设置,减少代码量。

 子View的宽度始终是父级View的一半(或者任意百分比)

其实这个很简单=。= 再看看这个公式:

1
viewA-attribute = viewB-attribute * multiplier + constant

这个是Autolayout里面一个约束的不同属性的基本组合关系,替换成宽度的话,就是下面这样:

1
子View的宽度 = 父级View宽度 * 系数 + 常数;

在Masonry里面,其实有个函数“multipliedBy”,就是用来设置multipler属性的(跟原本的NSLayoutConstraint的对应)。

关键代码

如下:

1
2
3
4
5
6
7
8
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
    //上下左贴边
    make.left.equalTo(_containerView.mas_left);
    make.top.equalTo(_containerView.mas_top);
    make.bottom.equalTo(_containerView.mas_bottom);
    //宽度为父view的宽度的一半
    make.width.equalTo(_containerView.mas_width).multipliedBy(0.5);
}];

接着,只要控制父级View的宽度,子View的宽度就会随着变化了。

小节

multipliedBy在Masonry的Github主页里面没有=。=



0 0
原创粉丝点击