瀑布流--教你如何自定义CollectionViewLayout

不废话,为了节省时间直接上gif图
传送门:https://github.com/JonesCxy/-

瀑布流.gif

附上我自定义CollectionViewLayout的代码:


#import <UIKit/UIKit.h>


@interface CxyCollectionViewLayout : UICollectionViewLayout

/**
 *  Grid cell size. Default value is (200, 100).
 */
@property (nonatomic) IBInspectable CGSize gridCellSize;

/**
 *  Parallax cell size. Default value is (400, 200).
 */
@property (nonatomic) IBInspectable CGSize parallaxCellSize;

/**
 *  Header size. Default value is (200, 200).
 *
 *  Set (0, 0) for no header
 */
@property (nonatomic) IBInspectable CGSize headerSize;

/**
 *  Size for more loader section. Default value is (50, 50).
 */
@property (nonatomic) IBInspectable CGSize moreLoaderSize;

/**
 *  Space between grid cells. Default value is (10, 10).
 */
@property (nonatomic) IBInspectable CGSize gridCellSpacing;

/**
 *  Padding for grid. Default value is 20.
 */
@property (nonatomic) IBInspectable CGFloat gridPadding;

/**
 *  Maximum parallax offset. Default value is 50.
 */
@property (nonatomic) IBInspectable CGFloat maxParallaxOffset;

/**
 *  Current orientation, used to layout correctly corresponding to orientation.
 */
@property (nonatomic) UIInterfaceOrientation currentOrientation;


@end

#import "CxyCollectionViewLayout.h"
#import "CxyCollectionView.h"

// 各项参数
#define GRID_CELL_SIZE CGSizeMake(200, 100)
#define GRID_CELL_SPACING CGSizeMake(10, 10)
#define GRID_PADDING 10
#define PARALLAX_CELL_SIZE CGSizeMake(400, 200)
#define HEADER_SIZE CGSizeMake(200, 200)
#define MORE_LOADER_SIZE CGSizeMake(50, 50)
#define NUMBER_OF_ITEMS_IN_GROUP 10
#define SECTION 0
#define MAX_PARALLAX_OFFSET 50

// For fixed layout, group 10 items to one group
// Each group has the following layout:
// x x
// x x
// ---
// x x
// x x
// x x
// ---
//
// Annotation:
// x: one cell
// ---: parallax effect
//
// Item at index 3 and 9 have parallax effect
// The rest separates into 2 grids:
// 1. 1 2
//    3 2
// 2. 1 2
//    3 3
//    4 5
//
// Notes:
// - Cell having the same number that is represent for item occuping multiple cells


@implementation CxyCollectionViewLayout
{

    CGSize _contentSize;
    CGSize _groupSize;
    CGSize _internalGridCellSize;
    CGSize _internalParallaxCellSize;
    CGSize _previousBoundsSize;
    NSMutableDictionary *_cellAttributes;
    UICollectionViewLayoutAttributes *_headerAttributes;
    UICollectionViewLayoutAttributes *_moreLoaderAttributes;


}




- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [self setDefaultValues];
    }
    
    return self;
}

- (instancetype)init {
    if (self = [super init]) {
        [self setDefaultValues];
    }
    
    return self;
}

- (void)prepareLayout {
    _internalGridCellSize = self.gridCellSize;
    _internalParallaxCellSize = self.parallaxCellSize;
    
    // Calculate content height
    [self calculateContentSize];
    
    // Calculate cell size
    [self calculateCellSize];
    
    // Calculate cell attributes
    [self calculateCellAttributes];
    
    // Calculate header attributes
    [self calculateHeaderAttributes];
    
    // Calculate more loader attributes
    [self calculateMoreLoaderAttributes];
}

- (CGSize)collectionViewContentSize {
    return _contentSize;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:10];
    NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:0];
    
    for (NSInteger itemCount = 0; itemCount < numberOfItems; itemCount++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:itemCount inSection:SECTION];
        UICollectionViewLayoutAttributes *attributes = [_cellAttributes objectForKey:indexPath];
        
        if (CGRectIntersectsRect(rect, attributes.frame)) {
            [result addObject:attributes];
        }
    }
    
    // Add header attributes to array if it is in rect
    if (_headerAttributes && CGRectIntersectsRect(rect, _headerAttributes.frame)) {
        [result addObject:_headerAttributes];
    }
    
    // Add more loader attributes to array if it is in rect
    if (_moreLoaderAttributes && CGRectIntersectsRect(rect, _moreLoaderAttributes.frame)) {
        [result addObject:_moreLoaderAttributes];
    }
    
    return result;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [_cellAttributes objectForKey:indexPath];
    
    return attributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
    return _headerAttributes;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    if (!CGSizeEqualToSize(_previousBoundsSize, newBounds.size)) {
        _previousBoundsSize = newBounds.size;
        
        return true;
    }
    
    return false;
}

#pragma mark - Calculation methods

- (void)calculateContentSize {
    NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:SECTION];
    
    if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
        _groupSize.width = self.collectionView.bounds.size.width;
        _groupSize.height = _internalGridCellSize.height * 6 + self.gridCellSpacing.height * 4 + _internalParallaxCellSize.height * 2 + self.gridPadding * 4;
        _contentSize.width = self.collectionView.bounds.size.width;
        _contentSize.height = _groupSize.height * (numberOfItems / 10);
    } else {
        
        _groupSize.width = _internalGridCellSize.width * 6 + self.gridCellSpacing.width * 4 + _internalParallaxCellSize.width * 2 + self.gridPadding * 4;
        _groupSize.height = self.collectionView.bounds.size.height;
        _contentSize.width = _groupSize.width * (numberOfItems / 10);
        _contentSize.height = self.collectionView.bounds.size.height;
    }
    
    NSInteger numberOfItemsInLastGroup = numberOfItems % 10;
    BOOL enableLoadMore = ((CxyCollectionView *) self.collectionView).enableLoadMore;
    
    if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
        if (numberOfItemsInLastGroup > 0) {
            _contentSize.height += _internalGridCellSize.height + self.gridPadding;
        }
        if (numberOfItemsInLastGroup > 1) {
            _contentSize.height += _internalGridCellSize.height + self.gridCellSpacing.height;
        }
        if (numberOfItemsInLastGroup > 3) {
            _contentSize.height += _internalParallaxCellSize.height + self.gridPadding;
        }
        if (numberOfItemsInLastGroup > 4) {
            _contentSize.height += _internalGridCellSize.height + self.gridPadding;
        }
        if (numberOfItemsInLastGroup > 6) {
            _contentSize.height += _internalGridCellSize.height * 2 + self.gridCellSpacing.height * 2;
        }
        if (numberOfItemsInLastGroup > 7) {
            _contentSize.height += _internalGridCellSize.height + self.gridCellSpacing.height;
        }
        _contentSize.height += self.headerSize.height;
        _contentSize.height += (enableLoadMore) ? self.moreLoaderSize.height : 0;
    } else {
        if (numberOfItemsInLastGroup > 0) {
            _contentSize.width += _internalGridCellSize.width + self.gridPadding;
        }
        if (numberOfItemsInLastGroup > 1) {
            _contentSize.width += _internalGridCellSize.width + self.gridCellSpacing.width;
        }
        if (numberOfItemsInLastGroup > 3) {
            _contentSize.width += _internalParallaxCellSize.width + self.gridPadding;
        }
        if (numberOfItemsInLastGroup > 4) {
            _contentSize.width += _internalGridCellSize.width + self.gridPadding;
        }
        if (numberOfItemsInLastGroup > 5) {
            _contentSize.width += _internalGridCellSize.width * 2 + self.gridCellSpacing.width * 2;
        }
        if (numberOfItemsInLastGroup > 7) {
            _contentSize.width += _internalGridCellSize.width + self.gridCellSpacing.width;
        }
        _contentSize.width += self.headerSize.width;
        _contentSize.width += (enableLoadMore) ? self.moreLoaderSize.width : 0;
    }
}

- (void)calculateCellSize {
    if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
        _internalGridCellSize.width = (self.collectionView.frame.size.width - self.gridCellSpacing.width - self.gridPadding * 2) / 2;
        _internalParallaxCellSize.width = self.collectionView.frame.size.width;
    } else {
        _internalGridCellSize.height = (self.collectionView.frame.size.height - self.gridCellSpacing.height - self.gridPadding * 2) / 2;
        _internalParallaxCellSize.height = self.collectionView.frame.size.height;
    }
}

- (void)calculateCellAttributes {
    NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:SECTION];
    
    _cellAttributes = [[NSMutableDictionary alloc] initWithCapacity:numberOfItems];
    for (NSInteger itemCount = 0; itemCount < numberOfItems; itemCount++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:itemCount inSection:SECTION];
        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        [_cellAttributes setObject:attributes forKey:indexPath];
    }
    
    CGFloat x = self.gridPadding;
    CGFloat y = self.gridPadding;
    
    // Give space for header
    if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
        y += self.headerSize.height;
    } else {
        x += self.headerSize.width;
    }
    
    for (NSInteger itemCount = 0; itemCount < numberOfItems; itemCount++) {
        NSInteger indexInGroup = itemCount % NUMBER_OF_ITEMS_IN_GROUP;
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:itemCount inSection:SECTION];
        UICollectionViewLayoutAttributes *attributes = [_cellAttributes objectForKey:indexPath];
        CGRect frame = CGRectZero;
        
        if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
            switch (indexInGroup) {
                case 0:
                    frame = CGRectMake(x, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 1:
                    frame = CGRectMake(x + _internalGridCellSize.width + self.gridCellSpacing.width, y, _internalGridCellSize.width, _internalGridCellSize.height * 2 + self.gridCellSpacing.height);
                    y += frame.size.height + self.gridPadding;
                    
                    break;
                case 2:
                    frame = CGRectMake(x, y - _internalGridCellSize.height - self.gridPadding, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 3:
                    frame = CGRectMake(0, y, _internalParallaxCellSize.width, _internalParallaxCellSize.height);
                    y += frame.size.height + self.gridPadding;
                    
                    break;
                case 4:
                    frame = CGRectMake(x, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 5:
                    frame = CGRectMake(x + _internalGridCellSize.width + self.gridCellSpacing.width, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    y += frame.size.height + self.gridCellSpacing.height;
                    
                    break;
                case 6:
                    frame = CGRectMake(x, y, _internalGridCellSize.width * 2 + self.gridCellSpacing.width, _internalGridCellSize.height * 2 + self.gridCellSpacing.height);
                    y += frame.size.height + self.gridCellSpacing.height;
                    
                    break;
                case 7:
                    frame = CGRectMake(x, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 8:
                    frame = CGRectMake(x + _internalGridCellSize.width + self.gridCellSpacing.width, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    y += frame.size.height + self.gridPadding;
                    
                    break;
                case 9:
                    frame = CGRectMake(0, y, _internalParallaxCellSize.width, _internalParallaxCellSize.height);
                    y += frame.size.height + self.gridPadding;
                    
                    break;
                    
                default:
                    break;
            }
        } else {
            switch (indexInGroup) {
                case 0:
                    frame = CGRectMake(x, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 1:
                    frame = CGRectMake(x + _internalGridCellSize.width + self.gridCellSpacing.width, y, _internalGridCellSize.width, _internalGridCellSize.height * 2 + self.gridCellSpacing.height);
                    
                    break;
                case 2:
                    frame = CGRectMake(x, y + _internalGridCellSize.height + self.gridCellSpacing.height, _internalGridCellSize.width, _internalGridCellSize.height);
                    x += _internalGridCellSize.width * 2 + self.gridCellSpacing.width + self.gridPadding;
                    
                    break;
                case 3:
                    frame = CGRectMake(x, 0, _internalParallaxCellSize.width, _internalParallaxCellSize.height);
                    x += frame.size.width + self.gridPadding;
                    
                    break;
                case 4:
                    frame = CGRectMake(x, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 5:
                    frame = CGRectMake(x + _internalGridCellSize.width + self.gridCellSpacing.width, y, _internalGridCellSize.width * 2 + self.gridCellSpacing.width, _internalGridCellSize.height * 2 + self.gridCellSpacing.height);
                    
                    break;
                case 6:
                    frame = CGRectMake(x, y + _internalGridCellSize.height + self.gridCellSpacing.height, _internalGridCellSize.width, _internalGridCellSize.height);
                    x += _internalGridCellSize.width * 3 + self.gridCellSpacing.width * 3;
                    
                    break;
                case 7:
                    frame = CGRectMake(x, y, _internalGridCellSize.width, _internalGridCellSize.height);
                    
                    break;
                case 8:
                    frame = CGRectMake(x, y + _internalGridCellSize.height + self.gridCellSpacing.height, _internalGridCellSize.width, _internalGridCellSize.height);
                    x += frame.size.width + self.gridPadding;
                    
                    break;
                case 9:
                    frame = CGRectMake(x, 0, _internalParallaxCellSize.width, _internalParallaxCellSize.height);
                    x += frame.size.width + self.gridPadding;
                    
                    break;
                    
                default:
                    break;
            }
        }
        attributes.frame = frame;
    }
}

- (void)calculateHeaderAttributes {
    if (self.headerSize.width == 0 || self.headerSize.height == 0) {
        return;
    }
    
    _headerAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CxyCollectionElementKindHeader withIndexPath:[NSIndexPath indexPathForRow:0 inSection:SECTION]];
    if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
        _headerAttributes.frame = CGRectMake(0, 0, self.collectionView.frame.size.width, self.headerSize.height);
    } else {
        _headerAttributes.frame = CGRectMake(0, 0, self.headerSize.width, self.collectionView.frame.size.height);
    }
}

- (void)calculateMoreLoaderAttributes {
    if (!((CxyCollectionView *) self.collectionView).enableLoadMore) {
        _moreLoaderAttributes = nil;
        
        return;
    }
    
    NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:SECTION];
    
    _moreLoaderAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CxyCollectionElementKindMoreLoader withIndexPath:[NSIndexPath  indexPathForRow:numberOfItems - 1 inSection:SECTION]];
    if (UIInterfaceOrientationIsPortrait(self.currentOrientation)) {
        _moreLoaderAttributes.frame = CGRectMake(0, _contentSize.height - self.moreLoaderSize.height, self.collectionView.frame.size.width, self.moreLoaderSize.height);
    } else {
        _moreLoaderAttributes.frame = CGRectMake(_contentSize.width - self.moreLoaderSize.width, 0, self.moreLoaderSize.width, self.collectionView.frame.size.height);
    }
}

#pragma mark - Private methods

- (void)setDefaultValues {
    _previousBoundsSize = CGSizeZero;
    self.gridCellSize = GRID_CELL_SIZE;
    self.parallaxCellSize = PARALLAX_CELL_SIZE;
    self.gridCellSpacing = GRID_CELL_SPACING;
    self.headerSize = HEADER_SIZE;
    self.moreLoaderSize = MORE_LOADER_SIZE;
    self.gridPadding = GRID_PADDING;
    self.maxParallaxOffset = MAX_PARALLAX_OFFSET;
}

@end


最后感谢博主资料([当你创建viewcontroller时忘记勾选创建xib文件后,如何单独创建xib详解]):
http://blog.csdn.net/u011005737/article/details/45060479

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容