私家博客搬运

swift:自定义UICollectionViewFlowLayout

新颖的需要中,需求已毕壹个显得国粹上新的视图,突显情势是点击2个UITableView的cell,在cell拉出二个空白的“抽屉”视图,“抽屉”中横向展示可滚动的七个宝贝的图文视图,于是很自然地想到用UICollectionView来兑现。后来那些须要被砍掉了(orz),但是UICollectionView是三个很有趣很灵敏的视图,类似Android的GridView,然则比之功力更强大,它能够切实到每三个item的定制,完全取决于UICollectionViewLayout的兑现。

编写目标

UICollectionView是ios中1个不行有力的控件,利用它能够万分简便的兑现部分很美观的功能。UICollectionView的职能又凭借于UICollectionViewLayout抑或它的子类UICollectionViewFlowLayout。而关于自定义UICollectionViewFlowLayout网上介绍的比较少。出于这一目标,写下那边文章,希望可以帮助初学者(小编也是)完结部分简练的流水布局功用。下边的演示就是本篇小说的目标。最终版代码和富有图片素材(图片名和档次中有点不一样等)已经上传至Github,大家可以下载学习。

图片 1

answer huang的译文中那样表明UICollectionView:

几个简单的定义

  • UICollectionViewLayout与UICollectionViewFlowLayout

UICollectionView的来得效果大致全部由UICollectionViewLayout肩负(甚至是cell的尺寸)。所以,一般开发中所说的自定义UICollectionView相当于自定义UICollectionViewLayout。而UICollectionViewFlowLayout是继承自UICollectionViewLayout的,由苹果官方已毕的流水布局作用。如果想自个儿落成部分水流布局效用可以一而再自最原始UICollectionViewLayout从头写,也足以持续自UICollectionViewFlowLayout举办改动。文本是一而再自UICollectionViewFlowLayt*

  • UICollectionViewLayoutAttributes

其次点就说了UICollectionView的来得效果大概任何由UICollectionViewLayout担当,而真正存储着每贰个cell的岗位、大小等品质的是UICollectionViewLayoutAttributes。各种cell对应着多少个属于自个儿的UICollectionViewLayoutAttributes,而UICollectionViewLayout多亏利用UICollectionViewLayoutAttributes里设有的音讯对每七个cel举办布局。

  • 流水布局

所谓流水布局就是:就是cell以一定的法则举办就如流水一般的有规律的三个随之贰个的排列。�最经典的流水布局便是九宫格布局,绝超过一半的图纸拔取器也是流水布局。

UITableView和UICollectionView都以由data-source和delegate驱动的。他们为其出示的子视图集扮演为愚昧的容器(dumb
containers),对他们实际的始末(contents)毫不知情。

居安虑危干活

  • xcode7.0
  • swift2.0
  • 温馨作者提供的素材并在控制器中添加如下代码

class ViewController: UIViewController,UICollectionViewDelegate, UICollectionViewDataSource {

    lazy var imageArray: [String] = {

        var array: [String] = []

        for i in 1...20 {
            array.append("\(i)-1")
        }

        return array
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        let collectionView =  UICollectionView(frame: CGRectMake(0, 100, self.view.bounds.width, 200), collectionViewLayout: UICollectionViewFlowLayout())
        collectionView.backgroundColor = UIColor.blackColor()
        collectionView.dataSource  = self
        collectionView.delegate = self

        collectionView.registerClass(ImageTextCell.self, forCellWithReuseIdentifier: "ImageTextCell")
        self.view.addSubview(collectionView)
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.imageArray.count;
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ImageTextCell", forIndexPath: indexPath) as! ImageTextCell
        cell.imageStr = self.imageArray[indexPath.item]

        return cell
    }

}
//这里是自定义cell的代码
class ImageTextCell: UICollectionViewCell {

    var imageView: UIImageView?
    var imageStr: NSString? {

        didSet {
            self.imageView!.image = UIImage(named: self.imageStr as! String)
        }

    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.imageView = UIImageView()
        self.addSubview(self.imageView!)

    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.imageView?.frame = self.bounds
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

效用应该是如此的

图片 2

UICollectionView进一步抽象了。它将其子视图的职分,大小和外观的控制权委托给多个单独的布局对象。通过提供1个自定义布局对象,你大致可以达成其余你能设想到的布局。布局继承自UICollectionViewLayout那个抽象基类。iOS6中以UICollectionViewFlowLayout类的方式提议了1个具体的布局已毕。

编码

有关UICollectionView,其余的同事已经介绍过啊,可以看那里

水平排列

开创3个名为LineLayout.swift的文书(继承自UICollectionViewFlowLayout)。添加如下几行代码

    var itemW: CGFloat = 100
    var itemH: CGFloat = 100

    override init() {
        super.init()

        //设置每一个元素的大小
        self.itemSize = CGSizeMake(itemW, itemH)
        //设置滚动方向
        self.scrollDirection = .Horizontal
        //设置间距
        self.minimumLineSpacing = 0.7 * itemW
    }

    //苹果推荐,对一些布局的准备操作放在这里
    override func prepareLayout() {
        //设置边距(让第一张图片与最后一张图片出现在最中央)ps:这里可以进行优化
        let inset = (self.collectionView?.bounds.width ?? 0)  * 0.5 - self.itemSize.width * 0.5
        self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset)
    }

成效就成了那样

图片 3

大家写一个简练的小demo,效果类似那样
UICollectionViewFlowLayout

shouldInvalidateLayoutForBoundsChange方法与layoutAttributesForElementsInRect方法关系

标题所写出的是相当紧要的两艺术,先看本人添加的如下测试代码

    /**
    返回true只要显示的边界发生改变就重新布局:(默认是false)
    内部会重新调用prepareLayout和调用
    layoutAttributesForElementsInRect方法获得部分cell的布局属性
    */
    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        print(newBounds)
        return true
    }

    /**
    用来计算出rect这个范围内所有cell的UICollectionViewLayoutAttributes,
    并返回。
    */
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        print("layoutAttributesForElementsInRect==\(rect)")
        let ret = super.layoutAttributesForElementsInRect(rect)
//        print(ret?.count)
        return ret
    }

为了表明,作者添加了多少个打印语句,在shouldInvalidateLayoutForBoundsChange重临值设置为true后,会发现layoutAttributesForElementsInRect格局调用十二分再三,大概是每滑动一点就会调用一回。观看打印音信可以发现许多秘密

  • 运转程序有如下打印

layoutAttributesForElementsInRect==(0.0, 0.0, 568.0, 568.0)

接近看不太懂,没事,尝试滑动。

  • 滑动

(0.5, 0.0, 320.0, 200.0) //这个是shouldInvalidateLayoutForBoundsChange方法的打印的newBounds
layoutAttributesForElementsInRect==(0.0, 0.0, 568.0, 568.0)//这个是layoutAttributesForElementsInRect打印的rect
(1.5, 0.0, 320.0, 200.0) 
layoutAttributesForElementsInRect==(0.0, 0.0, 568.0, 568.0)
(3.5, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(0.0, 0.0, 568.0, 568.0)
...

不费吹灰之力察觉,shouldInvalidateLayoutForBoundsChange的参数newBounds的意味是UICollectionView的可知矩形。什么叫可知矩阵?,因为UICollectionView也是UIScrollView的子类,所以它真的的“内容”远远不止大家屏幕上来看的那么多(那里不再话时间持续解释可知矩阵)。那好像layoutAttributesForElementsInRect打印出来的东西平素不吗变化是怎么回事?不急继续滑动。

  • 解密

持续滑动后有那几个消息,经过删除一些无用音讯,呈现如下。(注意看有注释的行)

...
(248.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(0.0, 0.0, 568.0, 568.0)
(249.0, 0.0, 320.0, 200.0)  //这里是可见矩阵
layoutAttributesForElementsInRect==(0.0, 0.0, 1136.0, 568.0)  //这里变化了1136.0是568.0的2倍(1136代表的是宽度的意思应该知道不需要解释吧)
(250.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(0.0, 0.0, 1136.0, 568.0)
...
(567.5, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(0.0, 0.0, 1136.0, 568.0)
(568.5, 0.0, 320.0, 200.0)//这里是可见矩阵
layoutAttributesForElementsInRect==(568.0, 0.0, 568.0, 568.0)  // 这里又变化了,x变成了568,宽度变成了568
(571.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(568.0, 0.0, 568.0, 568.0)
...
(815.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(568.0, 0.0, 568.0, 568.0)
(817.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(568.0, 0.0, 1136.0, 568.0) //还有这里
...
(1135.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(568.0, 0.0, 1136.0, 568.0)
(1136.0, 0.0, 320.0, 200.0)
layoutAttributesForElementsInRect==(1136.0, 0.0, 568.0, 568.0)  //还有这里

上边的的数目突显莫过于已经够用解释一切了。读到那里,推荐您本身去找找规律,通过协调发现的奥秘相相比较直接看本人写出答案有含义的多!下边那张图例已经表明了一切

图片 4

关于为啥会是568的翻番。。因为本人是用的5s模拟器。你换到4s就改成480了。至于那样设计的说辞,作者预计是为着便利开展界定的明确。

那是最简便的水流布局:UICollectionViewFlowLayout。类如其义,它提供3个水流布局,item(类似UITableView中的cell)会排列在上一个item的左边,即使显示屏空间不够,它会活动排到下一行,和流水一样。

缩放效果

了解了上面shouldInvalidateLayoutForBoundsChange方法与layoutAttributesForElementsInRect方法关系后,可以继续举办编码了。因为根本的情节早已讲解截至,剩下的就只是有的动画的估量,所以不再接续上课,直接贴出代码。

class LineLayout: UICollectionViewFlowLayout {

    var itemW: CGFloat = 100
    var itemH: CGFloat = 100

    lazy var inset: CGFloat = {
        //这样设置,inset就只会被计算一次,减少了prepareLayout的计算步骤
        return  (self.collectionView?.bounds.width ?? 0)  * 0.5 - self.itemSize.width * 0.5
        }()

    override init() {
        super.init()

        //设置每一个元素的大小
        self.itemSize = CGSizeMake(itemW, itemH)
        //设置滚动方向
        self.scrollDirection = .Horizontal
        //设置间距
        self.minimumLineSpacing = 0.7 * itemW
    }

    //苹果推荐,对一些布局的准备操作放在这里
    override func prepareLayout() {

        //设置边距(让第一张图片与最后一张图片出现在最中央)
        self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    /**
    返回true只要显示的边界发生改变就重新布局:(默认是false)
    内部会重新调用prepareLayout和调用
    layoutAttributesForElementsInRect方法获得部分cell的布局属性
    */
    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        return true
    }

    /**
    用来计算出rect这个范围内所有cell的UICollectionViewLayoutAttributes,
    并返回。
    */
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        //取出rect范围内所有的UICollectionViewLayoutAttributes,然而
        //我们并不关心这个范围内所有的cell的布局,我们做动画是做给人看的,
        //所以我们只需要取出屏幕上可见的那些cell的rect即可
        let array = super.layoutAttributesForElementsInRect(rect)

        //可见矩阵
        let visiableRect = CGRectMake(self.collectionView!.contentOffset.x, self.collectionView!.contentOffset.y, self.collectionView!.frame.width, self.collectionView!.frame.height)

        //接下来的计算是为了动画效果
        let maxCenterMargin = self.collectionView!.bounds.width * 0.5 + itemW * 0.5;
        //获得collectionVIew中央的X值(即显示在屏幕中央的X)
        let centerX = self.collectionView!.contentOffset.x + self.collectionView!.frame.size.width * 0.5;
        for attributes in array! {
            //如果不在屏幕上,直接跳过
            if !CGRectIntersectsRect(visiableRect, attributes.frame) {continue}
            let scale = 1 + (0.8 - abs(centerX - attributes.center.x) / maxCenterMargin)
            attributes.transform = CGAffineTransformMakeScale(scale, scale)
        }

        return array
    }

    /**
    用来设置collectionView停止滚动那一刻的位置

    - parameter proposedContentOffset: 原本collectionView停止滚动那一刻的位置
    - parameter velocity:              滚动速度

    - returns: 最终停留的位置
    */
    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        //实现这个方法的目的是:当停止滑动,时刻有一张图片是位于屏幕最中央的。

        let lastRect = CGRectMake(proposedContentOffset.x, proposedContentOffset.y, self.collectionView!.frame.width, self.collectionView!.frame.height)
        //获得collectionVIew中央的X值(即显示在屏幕中央的X)
        let centerX = proposedContentOffset.x + self.collectionView!.frame.width * 0.5;
        //这个范围内所有的属性
        let array = self.layoutAttributesForElementsInRect(lastRect)

        //需要移动的距离
        var adjustOffsetX = CGFloat(MAXFLOAT);
        for attri in array! {
            if abs(attri.center.x - centerX) < abs(adjustOffsetX) {
                adjustOffsetX = attri.center.x - centerX;
            }
        }

        return CGPointMake(proposedContentOffset.x + adjustOffsetX, proposedContentOffset.y)
    }
}

如果在控制器中加入下边三个点子,在您点击控制器,或然点击有些cell会有很炫的动画产生,那都以苹果帮大家做好的。

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

        self.imageArray.removeAtIndex(indexPath.item)

        collectionView.deleteItemsAtIndexPaths([indexPath])
    }

        override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {

        if self.collectionView!.collectionViewLayout.isKindOfClass(LineLayout.self) {
            self.collectionView!.setCollectionViewLayout(UICollectionViewFlowLayout(), animated: true)
        }else {
            self.collectionView!.setCollectionViewLayout(LineLayout(), animated: true)
        }

    }

代码如下:

总结

本篇小说记录了自我在自定义UICollectionViewFlowLayout进程中相见的一些题目和解决方式(其实有部分坑爹的难题本人未曾列出,怕误导我们)。上边的全体都以基于UICollectionViewFlowLayout拓展的转移。而自作者在GitHub上边上传的也有一份继承自UICollectionViewLayout的非流水布局。效果如下,因为原理性的事物都几乎,就不再举行解析(代码也有注释)。感兴趣的能够那Github地方下载。倘若文章中有如何错误可能更好的艺术、提议等等,感激您的提议。我们一道学习!O(∩_∩)O!

图片 5

  • (UICollectionView *)collectionView
    {
    if (!_collectionView) {
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout
    alloc] init];
    self.currentLayout = layout;
    _collectionView = [[UICollectionView alloc]
    initWithFrame:CGRectMake(0, 150, [UIScreen
    mainScreen].bounds.size.width, 240)
    collectionViewLayout:layout];
    _collectionView.delegate = self;
    _collectionView.dataSource = self;
    [_collectionView registerClass:[ZQCollectionViewCell class]
    forCellWithReuseIdentifier:@”cell”];
    }

    return _collectionView;
    }

后记

自小编不会报告您,介绍UICollectionView的自定义布局那篇文章,是自个儿下3个实验的前传。可是目前被老师强迫帮他们去写文档,推断进程得放缓。

大家明天想落成一个如此的功能:
当左右拖动item的时候,靠近显示屏宗旨item放大,远离主题的item收缩
反正滑动的时候,截至下来的item的基本永远和显示器中央对齐

意义如图:
linear

第①,我们须求自定义UICollectionViewLayout。由于须求效果依旧维持流水性格,大家接纳继承UICollectionViewFlowLayout。须要重写的点子如下:

  • (void)prepareLayout:该办法是历次布局时有些准备工作,可以在那里做一些初阶化的操作,记得调用super完结!
  • (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds:当布局的境界发送改变的时候,会询问该方法是或不是再度布局
  • (NSArray
    *)layoutAttributesForElementsInRect:(CGRect)rect:重返可知的rect中负有的Elements的布局属性(UICollectionViewLayoutAttributes)。在UICollectionViewLayoutAttributes可安装item的size、center、transform等等属性,来规范定制item的岗位和形变.UICollectionViewFlowLayout对该措施有1个暗许达成,可使item流水式排列。而UICollectionViewLayout则完全是空完结,要求我们团结总计
  • (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
    withScrollingVelocity:(CGPoint)velocity:当item被滑动自行甘休时,我们得以依据那几个主意设定item停在我们希望的地点

代码如下:

  • (void)prepareLayout
    {
    [super prepareLayout];
    // 每个item的size
    self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
    // 最小间距
    self.minimumLineSpacing = 50;
    // 横向滚动
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    // 内间距
    self.sectionInset = UIEdgeInsetsMake(90,
    (self.collectionView.bounds.size.width – ITEM_SIZE) * 0.5, 40,
    150);
    }

  • (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
    {
    return YES;
    }

  • (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
    withScrollingVelocity:(CGPoint)velocity
    {
    // 当前显示器的rect
    CGRect visiableRect = {proposedContentOffset,
    self.collectionView.bounds.size};
    CGFloat currentCenterX = proposedContentOffset.x +
    self.collectionView.bounds.size.width * 0.5;
    // 调用super完成可以拿走到当下rect中保有item的布局属性
    NSArray *attributesArray = [super
    layoutAttributesForElementsInRect:visiableRect];
    CGFloat adjustDistance = MAXFLOAT;
    // 总括距离核心点如今的item,来算偏移量
    for (UICollectionViewLayoutAttributes *attributes in
    attributesArray) {
    if (CGRectIntersectsRect(attributes.frame, visiableRect)) {//
    在显示屏内
    CGFloat x = attributes.center.x;
    if (ABS(x – currentCenterX) < ABS(adjustDistance)) {
    adjustDistance = x – currentCenterX;
    }
    }
    }

    return CGPointMake(proposedContentOffset.x + adjustDistance,
    proposedContentOffset.y);
    }

  • (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
    CGRect visiableRect = {self.collectionView.contentOffset,
    self.collectionView.bounds.size};
    CGFloat currentCenterX = self.collectionView.contentOffset.x +
    self.collectionView.bounds.size.width * 0.5;
    NSArray *attributesArray = [super
    layoutAttributesForElementsInRect:visiableRect];
    for (UICollectionViewLayoutAttributes *attributes in
    attributesArray) {
    if (CGRectIntersectsRect(attributes.frame, visiableRect)) {//
    在屏幕内
    CGFloat itemCenterX = attributes.center.x;
    // 设定八个推广的周详,公式大家可以自个儿来定,效果类似就可以
    CGFloat scale = 1 + SCALE_FACTOR * (1 – (ABS(itemCenterX –
    currentCenterX) / SCALE_BIG_DISTANCE));
    attributes.transform3D = CATransform3DMakeScale(scale, scale,
    1.0);
    }
    }
    return attributesArray;
    }

如上是继承UICollectionViewFlowLayout的布局,如果大家想做四回更深层次的定制,大家可以直接继承UICollectionViewLayout来完全自定义布局,要重写的格局和UICollectionViewFlowLayout类似,只是大家须求自身来促成那一个点子:

  • (NSArray
    *)layoutAttributesForElementsInRect:(CGRect)rect:必要自身新建UICollectionViewLayoutAttributes,也足以透过上边的措施2完事
  • (UICollectionViewLayoutAttributes
    *)layoutAttributesForItemAtIndexPath:(NSIndexPath
    *)indexPath:该措施确定当前indexPath下的item的布局属性

代码如下:

  • (void)prepareLayout
    {
    [super prepareLayout];
    }

  • (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
    {
    return YES;
    }

  • (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
    NSInteger count = [self.collectionView
    numberOfItemsInSection:0];
    NSMutableArray *attributesArray = [NSMutableArray array];
    for (NSInteger i = 0; i < count; ++i) {
    UICollectionViewLayoutAttributes *attributes = [self
    layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i
    inSection:0]];
    [attributesArray addObject:attributes];
    }
    return attributesArray;
    }

  • (UICollectionViewLayoutAttributes
    *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
    NSInteger count = [self.collectionView
    numberOfItemsInSection:0];
    CGPoint currentCenter =
    CGPointMake(self.collectionView.contentOffset.x +
    self.collectionView.bounds.size.width * 0.5,
    self.collectionView.bounds.size.height * 0.5);
    UICollectionViewLayoutAttributes *attributes =
    [UICollectionViewLayoutAttributes
    layoutAttributesForCellWithIndexPath:indexPath];
    attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);

    CGFloat angelDelta = M_PI * 2 / count;
    CGFloat angel = M_PI_2 – angelDelta * indexPath.row;
    attributes.center = CGPointMake(currentCenter.x + CIRCLE_RADIUS *
    cosf(angel), currentCenter.y + CIRCLE_RADIUS * sinf(angel));
    return attributes;

}
上述可已毕二个环形(circle)布局,
demo中还添加了点击item就把该item删除的风浪
circel

还足以做一个像样蜂巢的布局:

  • (CGSize)collectionViewContentSize
    {
    NSInteger count = [self.collectionView
    numberOfItemsInSection:0];
    return CGSizeMake(0, (count / COL)* count);
    }

  • (UICollectionViewLayoutAttributes
    *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
    UICollectionViewLayoutAttributes *attributes =
    [UICollectionViewLayoutAttributes
    layoutAttributesForCellWithIndexPath:indexPath];

    UICollectionView *collection = self.collectionView;

    float x = (SIZE + self.margin) * (indexPath.item % COL + 1) *
    0.75 + 40;
    float y = (SIZE + self.margin) * (indexPath.item / COL + 0.5) *
    cos(M_PI * 30.0f / 180.0f) + 20;
    if (indexPath.item % 2 == 1) {
    y += (SIZE + self.margin) * 0.5 * cosf(M_PI * 30.0f / 180.0f);
    }

    attributes.center = CGPointMake(x + collection.contentOffset.x, y +
    collection.contentOffset.y);
    attributes.size = CGSizeMake(SIZE, SIZE * cos(M_PI * 30.0f /
    180.0f));

    return attributes;
    }

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *arr = [super layoutAttributesForElementsInRect:rect];
if ([arr count] > 0) {
return arr;
}
NSMutableArray *attributes = [NSMutableArray array];
for (NSInteger i = 0 ; i < [self.collectionView
numberOfItemsInSection:0 ]; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i
inSection:0];
[attributes addObject:[self
layoutAttributesForItemAtIndexPath:indexPath]];
}
return attributes;
}

其中cell的样式:

  • (instancetype)initWithFrame:(CGRect)frame

{

if (self = [super initWithFrame:frame]) {

    self.backgroundColor = ZQRandomColor;

    CGFloat longSide = SIZE * 0.5 * cosf(M_PI * 30 / 180);

    CGFloat shortSide = SIZE * 0.5 * sinf(M_PI * 30 / 180);

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointMake(0, longSide)];

    [path addLineToPoint:CGPointMake(shortSide, 0)];

    [path addLineToPoint:CGPointMake(shortSide + SIZE * 0.5, 0)];

    [path addLineToPoint:CGPointMake(SIZE, longSide)];

    [path addLineToPoint:CGPointMake(shortSide + SIZE * 0.5, longSide * 2)];

    [path addLineToPoint:CGPointMake(shortSide, longSide * 2)];

    [path closePath];



    CAShapeLayer *maskLayer = [CAShapeLayer layer];

    maskLayer.path = [path CGPath];



    self.layer.mask = maskLayer;

}

return self;

}

意义如图:
honeycomb