zoukankan      html  css  js  c++  java
  • 【转】有趣的Autolayout示例-Masonry实现

    原文网址:http://tutuge.me/2015/05/23/autolayout-example-with-masonry/

    好久没有写Blog了,这段时间有点忙啊=。=
    本文举了3个比较有“特点”的Autolayout例子,源于微博上好友的提问,感觉比较有意思,也比较有代表性,就写了出来,分享给大家~
    至于为什么用Masonry,那是因为它好用啊!(被问到过有关Masonry的问题,就索性用它来实现吧=。=)。

    效果图

    效果图

    Github地址

    https://github.com/zekunyan/AutolayoutExampleWithMasonry

    关于例子工程结构

    实现的时候采用的是用StoryBoard拖拽约束+Masonry手写代码相结合的方式实现。最关键的地方是用Masonry,为了更好地突出重点。其它的无关紧要的空间约束,直接就拖拽了。

    关于Autolayout

    刚开始学习Autolayout的时候,什么“Leading Edges”、“Horizontal Centers”,好多啊,感觉一下子适应不来,有时候面对一个界面布局上的需求,可能都无从下手。

    总的来说,我觉得Autolayout的关键就是“Constraint(约束)”。其实就是以下两点:

    1. 从显式设置frame的属性,到利用约束控制View的大小、位置。
    2. 思考如何布局时,重点从单个的View,到整体所有View之间的相互关系。

    既然没有了具体设置View的frame属性,也就是说,系统会在运行时,通过我们设定的“约束”,计算出每个View的frame,再去绘制屏幕内容。

    也就是说,我们设置的Constraint,要能体现出View的位置(x、y坐标)大小(宽高)。无论是用IB拖拽约束,还是手写代码,只要从这个角度去思考,很多问题就都能解决。

    有关Autolayout的知识,网上有很多,在这里就不详细列出了,但是有个公式倒是可以贴出来:

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

    关于Masonry

    好用!

    Case 1: 并排两个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”属性的高低决定。

    约束示例图

    约束示例图

    关键代码

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

    设置位置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 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
    13
    14
    15
    //设置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”属性。

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

    先看看示例的截图:

    效果图

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

    分析

    1. 首先就是整体居中,为了实现这个,最简单的办法就是将四个图片“装进”一个容器View里面,然后让这个容器View在整个页面中居中即可。这样就不用控制每个图片的居中效果了。
    2. 然后就是显示与隐藏。在这里我直接控制图片ImageView的宽度,宽度为0的时候不就“隐藏”了吗。

    约束示例图

    约束示例图

    解释

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

    1. 尽量减少无效的约束,保证约束不多也不少。
    2. 内部的每个imageView约束其实都只有四个:left、centerY、width和height,这样有个好处,就是可以在循环里面依次添加约束,大大减少代码量。
    3. 最右边的imageView还要单独设置跟容器View的右边约束,是为了不用设置容器View的width,保证容器View是刚好包含内部的View的,这样整体才是居中的。

    关键代码

    先看看最外层容器View的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //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
    //循环创建、添加imageView
    for (NSUInteger i = 0; i < 4; i++) {
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:_imageNames[i]]];
    [_imageViews addObject:imageView];
    [_containerView addSubview:imageView];
    }

    最后是循环对imageView加上约束: 感谢XVXVXXX的PR:-D

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //设定大小
    CGSize imageViewSize = CGSizeMake(IMAGE_SIZE, IMAGE_SIZE);

    //分别设置每个imageView的宽高、左边、垂直中心约束,注意约束的对象
    //每个View的左边约束和左边的View的右边相等=。=,有点绕口...

    // 保存循环中的临时结果
    __block UIView *lastView = nil;
    __block MASConstraint *widthConstraint = nil;

    NSUInteger arrayCount = _imageViews.count;
    [_imageViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
    //宽高固定
    widthConstraint = make.width.equalTo(@(imageViewSize.width));
    make.height.equalTo(@(imageViewSize.height));
    //左边约束
    make.left.equalTo(lastView ? lastView.mas_right : view.superview.mas_left);
    //垂直中心对齐
    make.centerY.equalTo(view.superview.mas_centerY);
    //设置最右边的imageView的右边与父view的最右对齐
    if (idx == arrayCount - 1) {
    make.right.equalTo(view.superview.mas_right);
    }

    [_widthConstraints addObject:widthConstraint];
    lastView = view;
    }];
    }];

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (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,往往会起到事半功倍的效果。而且在组织约束的时候,尽量的将约束统一起来,这样可以用一个函数去设置,减少代码量。

    Case 3: 子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
    9
    [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主页里面没有=。=
    所以要养成读头文件的习惯~

    总结

    有关Autolayout的东西还有好多没有写,什么动画啊、动态修改约束之类的,本文也算是个引子吧,任重而道远~

    能看到这的朋友,也算是很有耐心了,哈哈~~

    参考

  • 相关阅读:
    ubuntu12.04 安装opencv
    VC warning C4786
    su root 后还是不能使用useradd ,useradd 等命令
    C++数组
    C++多维数组
    Centos7 GUI卸载安装gnome
    linux离线安装软件(三)——Centos7以源码编译方式安装两个版本gcc
    yum和源码编译安装nginx
    Linux修改移动硬盘文件类型
    Django部署时STATIC/MEDIA配置
  • 原文地址:https://www.cnblogs.com/wi100sh/p/5618945.html
Copyright © 2011-2022 走看看