zoukankan      html  css  js  c++  java
  • 使用Masonry在UIScrollView内布局

    理论分析

    首先,我们知道Autolayout改变了传统的以frame为主的布局思想。它其实是一种相对布局,核心思想是视图与视图之间的位置关系。比如,我们可以根据矩形的起始横坐标、纵坐标、长和宽这四个变量确定它的位置。或者,如果已经确定矩形A的位置,只要知道矩形B每条边的和A对应边之间的距离,也能确定B的位置。前者就是frame的思想,它基于绝对数值,而后者是Autolayout的思想,它基于偏移量的概念。

    其次,UIScrollView有自己的frame也就是我们在屏幕上能看到的区域。它还有一个contentSize的概念。在使用frame布局的时候,我们一般先设置好子视图的位置,最后再设置contentSize,它会将所有的子视图包含在内。于是通过滑动,我们就可以在有限的布局中,看到所有的内容了。

    但是在Autolayout时代,为了简化布局,我们希望contentSize能够自动设置。比如有一个scrollView,它有两个子视图。frame分别为(x: 0, y: 0, 10, height: 10)和(x: 10, y: 0, 10, height: 10),那么我们自然会认为这两个视图左右并排排列,contentSize为(x: 0, y: 0, 20, height: 10):


    自动计算contentSize

    这种把若干个子视图合并,得出contentSize的能力,人类是天生具备的,但是计算机却不是这样。仅凭以上信息,程序无法推断出真正的contentSize。原因在于,我们没有明确的告诉系统,在这两个子视图拼接而成的区域以外,还有没有区域应该被contentSize包含。

    也就是说,contentSize也有可能是下图中的阴影部分:


    更大的contentSize

    如果需要指定contentSize就是两个正方形拼接而成的区域,我们还需要提供四个信息:

    1. 左边的正方形的左侧的边,距离contentSize左边的距离为0
    2. 右边的正方形的右侧的边,距离contentSize右边的距离为0

    ……

    通过以上的分析,我们可以看到,其实contentSize是依赖于子视图自身的大小,和上下左右四个方向的留白大小计算出的。而UIScrollView的leading/trailing/top/bottom是相对于它的contentSize而不是bounds来确定的。所以如果你写这样的代码,布局是肯定不会生效的:

    subview.snp_makeConstraints { (make) -> Void in
        make.edges.equalTo(scrollView).offset(5)
    }

    因为我们其实是在根据UIScrollView的leading/trailing/top/bottom来确定子视图的位置,而我们已经分析过,UIScrollView的leading/trailing/top/bottom是相对于自己的contentSize而言的。而contentSize又是根据子视图位置决定的。这就变成了一种你依赖我,我又依赖你的情况。

    为了打破这种循环依赖,为子视图添加约束的两个要求是:

    1. 它不依赖于任何与scrollview有关布局,也就是不能参考scrollview的位置和大小。
    2. 它不仅要确定过自己的大小,还要确定自己与contentSize四周的距离。

    第二个要求意思是说,正常使用autolayout时,我们确定一个矩形在水平方向上的范围,只要知道它的左边距离它左边的矩形有多远,以及它有多宽即可。但是在UIScrollView中布局时,还需要告诉UIScrollView,它的右边距离右边的视图有多远。这样contentSize才能确定。否则UIScrollView就不知道contentSize向右可以延伸多少。在竖直方向上也是同理。

    这两大要求一定要牢记!接下来我们的代码都将围绕如何满足这两大要求展开。

    动手实践

    明白了问题的理论背景后,我们通过一个具体的需求,来看看正确的代码怎么写,以下面这个效果为例:


    任务目标

    如图所示,中间是一个UIScrollView,它的背景颜色是黄色。红色部分我们称之为box,它是一个普通的,红色背景的UIView。也就是说我们向UIScrollView中添加了多个box,每个子box之间间隔一定距离。我们分步实现这个功能

    使用container

    首先我们介绍一种使用Container的方法。

    第一步:为scrollView添加约束

    let scrollView = UIScrollView()
    view.addSubview(scrollView)
    scrollView.snp_makeConstraints { (make) -> Void in
        make.centerY.equalTo(view.snp_centerY)
        make.left.right.equalTo(view)
        make.height.equalTo(topScrollHeight)
    }

    我们之前说过,使用Autolayout时,不用考虑frame布局。所以直接创建一个scrollView对象。需要先把scrollView添加到父视图上才能添加约束。

    scrollView添加约束没有什么难点,就像我们给其他视图添加约束一样。这里表示scrollView和父视图左右对齐,居中显示。

    第二步:为container添加约束

    scrollView.addSubview(containerView)
    containerView.snp_makeConstraints { (make) -> Void in
        make.edges.equalTo(scrollView)
        make.height.equalTo(topScrollHeight)
    }

    这里对container的约束非常重要,第一个约束表示自己上、下、左、右和contentSize的距离为0,因此只要container的大小确定,contentSize也就可以确定了,因为此时它和container大小、位置完全相同。

    第二个约束直接通过一个数值,确定container的高度。避免了依赖scrollview布局。这样一来,scrollview就变成水平的了。container的宽度直接决定了scrollview的宽度。

  • 相关阅读:
    完全背包详解
    0-1背包详解
    优先队列 + 模拟
    循环节 + 矩阵快速幂
    并查集 + 路径压缩(经典) UVALive 3027 Corporative Network
    dp
    动态规划分类(完整版)
    (二)Spring框架之JDBC的基本使用(p6spy插件的使用)
    (一)Spring框架基础
    (十六)客户端验证与struts2中的服务器端验证
  • 原文地址:https://www.cnblogs.com/hello-Huashan/p/5170061.html
Copyright © 2011-2022 走看看