CollectionView自定义风火轮 layout (一)

最终效果如下所示:

demo.gif

这个效果是我们公司的一个模块的效果, 当时没有由于没有对 collectionView 仔细研究,所以对这个界面的实现机制并不是很熟悉, 到现在已经有段时间了, 这段时间对 collectionView 也加深了解了一些, 于是试着自己写一下试试(当时使我们公司一个大牛写的)

我打算分一下几步来实现这个效果:

  1. 实现圆形布局(这个布局效果在 Apple 的实例代码中有, 具体代码请自行 Google)
  2. 实现圆形的风火轮效果
  3. 对有些需要隐藏的位置进行隐藏

环形布局之前Apple 提供的代码中是直接根据角度计算的每个 Item 的位置, 我们也用同样的思考, 不同的是我们要将角度记录下来, 这个角度是跟 collectionView 的 contentOffset 有关的, 因为当用户在滑动的时候, contentOffset 在更新,这个时候应该重新根据 contentOffset 计算每个 Item 的角度 — 在心中有个印象

  1. 创建自定义布局

定义好半径大小之后, 我们还需要个属性 相邻两个 Item之间的夹角是多少度于是我们在 extension 中定义 anglePerItem属性, 存储夹角, 并在 initial 中做初始化

我们之前说过, 每个 Item 要有一个 angle, 用来确定在 contentOffset 时, 对应的 item 的角度是多少, 所以这个时候我们需要自定义 LayoutAttributes

自定义 LayoutAttributes

回到 Layout 类

因为我们自定义了 Attributes 类, 所以此时要告知 Layout 类, 我们自定义的 Attributes

因为需要用户去滑动, 又因为 CollectionView 继承自 ScrollView, 运行滑动的一个必要条件就是 contentSize某一个方向的值大于 scrollView.bounds 对应方向的值

好了准备工作基本完成, 接下来开始布局

在这里必须要了解 collectionView 的布局步骤

  1. prepareLayout 每次布局触发时,就会调用该方法
  2. layoutAttributesForElementsInRect:(CGRect)rect 返回在 rect 矩形内的 item 的布局属性数组
  3. layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 返回在某个 indexPath 的 item 的布局属性

我们需要一个布局属性数组, 来存储所有 item 的布局属性

于是我们在 extension 中添加一个布局属性数组
@interface CircleCollectionViewLayout ()
@property (nonatomic, assign) CGFloat anglePerItem;
@property (nonatomic, copy) NSArray *attributesList;
@end

我们直接在layoutAttributesForElementsInRect中返回该数组, 因为我将要在 prepareLayout 中将该数组填充进布局属性的值

同理我们直接将某个位置的布局属性从 attributesList 中取出

OK, 开始进行布局

如下图:

看来我们的思路是正确的, 接下来, 我们所需要的就是在 prepareLayout 中进行布局, 使布局更接近我们的目标效果

  1. 先形成圆形布局, 这个容易, 我们首先需要调整锚点, 将锚点调整的屏幕中间, 半径我们之间就定义过了, 屏幕宽度减去一个间隙的一半, 我们将目光放在第一个 Item, 要将第一个 item 放在屏幕下方, 同时锚点应该处于屏幕正中间, 所以锚点的 y 值应小于0, 锚点又是相对于自身的高度来的推出锚点的计算公式

效果如图所示

我们发现整个圆弧向上偏移了, 所以接下来就是调整每个 item 的中心点, 是之下移
同样在 for 循环中, 修改设置 center 的值

OK, 圆环效果成功做出, 第一步 OK, 细心的同学发现, 界面上显示的 Item 并不是从0开始, 那么试着将 numberOfItem 改成 8, 此时就是 0~8 显示, 之前之所以不是从零开始, 是因为我们的圆环一次最多显示8个, 而我们的 numberOfItem 有13个, 导致之后的 item 将前面的 item 覆盖

接下来我们实现滑动

滑动是跟 contentOffset 有关, 同时我们还需要设置一个方法

要想达到项目中滑动的效果, 我们需要设置 Item 布局属性的 angle, 并且这个 angle 是与 contentOffset 有关的

先来几条准备知识

  1. 以第0个 item 为起点, 它的角度此时为0度, 当滑动到最后一个 item 时, 我们让最后一个 item 位置与第0个位置重合, 此时第0个 item 总共经过了 -(numberOfItem * anglePerItem), 因为是逆时针转动, 所以是负值
  2. 由 1. 我们得到滑动到最后, 第0个 item 总共偏移了多少角度, 所以我们很容易得到单位偏移的角度, 总偏移角度 * (contentOffset.x 所占的比例)

由以上两点产生两个属性

修改 prepareLayout 中布局属性的 angle, 使之与 contentOffset 建立联系

效果如下

可以滑动
接下来, 我们进行最后的完善, 定义两个属性 startIndex, endIndex

在上面的 prepareLayout 中我们添加了一个 if-else, 目的是当 item 的角度小于某个值时将其隐藏, 因为是逆时针转动, 所以角度是成减小趋势, 当隐藏一个 item 时, 要多布局一个 item, 即 endIndex++, 显示同理, 根据 contentOffset 设置 alpha

但这是会发现, 最后一个 item 可以被滑动的不见, 我们只需要调整一个地方即可, 及第0个 item 的总偏移量, 因为他是根据个数, 让其减去5个 item, 此时便可达到效果, 需要确保总数 > 5

如图所示

第一部分只完成 collectionView 布局, 在下一部分讲解, 选择 item 进行切换的效果

我觉得这个布局可以优化, 但目前还没来得及, 如果您有更好的方式, 欢迎交流; 如果您有不明白的地方欢迎提问; 如果您有不满意的地方, 欢迎吐槽; 共同学习, 共同进步

Demo 地址: https://github.com/X-Liang/CircleCollectionView.git

1 5 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部