自定义CollectionView UICollectionViewLayout实现横向布局分页Emoji

需求:emoji的横向显示,在每页的最后需要显示一个删除按钮,如下图所示。

当然还有当数据更改移动删除时需要覆盖的方法,目前没有此需求,先不谈,具体可以看文档O(∩_∩)O~~ ####prepareLayout 在collectionview显示或更新时总会先调用此方法,该方法的默认实现什么都不做。子类可以覆盖它,并使用它来设置数据结构或执行以后执行布局所需的任何初始计算。 ####清楚了流程,代码很简单

//  BIEmojiCollectionViewLayout.h
#import <UIKit/UIKit.h>

@interface BICollectionViewFixedSizeLayout : UICollectionViewLayout

@property (nonatomic) CGSize itemSize;//item的size
@property (nonatomic) NSUInteger numberOfLinesPerPage;//每页的行数
@property (nonatomic) UIEdgeInsets contentInsets;//列表的内边距
@property (nonatomic) CGFloat minimalColumnSpacing;//item之间的间隙
@property (nonatomic) BOOL anchorLastItemPerPage;//是否每页最后一项是特殊的item,也就是需求中的删除按钮

- (BOOL)isLastItemPerPageAtIndexPath:(NSIndexPath *)indexPath;//获取当前indexPath是否是特殊的item

@end


 //BIEmojiCollectionViewLayout.m
#import "BICollectionViewFixedSizeLayout.h"

#import "BICollectionViewFixedSizeLayout.h"

@implementation BICollectionViewFixedSizeLayout {
    NSMutableDictionary<NSIndexPath *,UICollectionViewLayoutAttributes *> *_layoutAttributes;
    CGSize _contentSize;
    NSUInteger _numberOfColumnsPerPage;
}

#pragma mark - life cycle methods

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

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

#pragma mark - public methods

- (BOOL)isLastItemPerPageAtIndexPath:(NSIndexPath *)indexPath {
    if (self.anchorLastItemPerPage) {
        if ((indexPath.row + 1) % (_numberOfColumnsPerPage * _numberOfLinesPerPage) == 0 ||
            indexPath.row == [self.collectionView numberOfItemsInSection:0] - 1) {
            return YES;
        }
    }
    return NO;
}

#pragma mark - private methods

- (void)commonInit {
    _itemSize = CGSizeMake(40, 40);
    _numberOfLinesPerPage = 3;
    _anchorLastItemPerPage = NO;
    _minimalColumnSpacing = 1.0;
    _contentInsets = UIEdgeInsetsMake(5, 5, 5, 5);
    _layoutAttributes = [NSMutableDictionary dictionary];
}

#pragma mark - custom UICollectionViewLayout methods

- (void)prepareLayout {
    [super prepareLayout];
    //clean up
    [_layoutAttributes removeAllObjects];
    _contentSize = CGSizeZero;
    //caculate attibutes
    CGFloat collectionViewWidth = CGRectGetWidth(self.collectionView.bounds);
    CGFloat collectionViewHeight = CGRectGetHeight(self.collectionView.bounds);
    
    CGFloat itemWidth = _itemSize.width;
    CGFloat itemHeight = _itemSize.height;
    _numberOfColumnsPerPage = (collectionViewWidth - _contentInsets.left - _contentInsets.right + _minimalColumnSpacing) / (_itemSize.width + _minimalColumnSpacing);
    
    NSAssert([self.collectionView numberOfSections] == 1, @"number of sections should equal to 1.");
    NSUInteger numberOfItemsPerPage = _numberOfColumnsPerPage * _numberOfLinesPerPage;
    CGFloat columnSpacing = _numberOfColumnsPerPage == 1 ? 0.0 : (collectionViewWidth - _contentInsets.left - _contentInsets.right - _numberOfColumnsPerPage * itemWidth) / (_numberOfColumnsPerPage - 1);
    CGFloat lineSpacing = _numberOfLinesPerPage == 1 ? 0.0 : (collectionViewHeight - _contentInsets.top - _contentInsets.bottom - _numberOfLinesPerPage * itemHeight) / (_numberOfLinesPerPage - 1);
    NSUInteger numberOfRows = [self.collectionView numberOfItemsInSection:0];
    for (NSUInteger row = 0; row < numberOfRows; row++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
        NSUInteger currentPage = floor(row / numberOfItemsPerPage);
        NSUInteger currentItemOnPageIndex = (row % numberOfItemsPerPage);
        //
        CGFloat originX = 0.0;
        CGFloat originY = 0.0;
        if (_anchorLastItemPerPage && (row == numberOfRows - 1)) {
            originX = (currentPage + 1) * collectionViewWidth - _contentInsets.right - itemWidth;
            originY = collectionViewHeight - _contentInsets.bottom - itemHeight;
        } else {
            originX = _contentInsets.left + (row % _numberOfColumnsPerPage) * (columnSpacing + itemWidth) + currentPage * collectionViewWidth;
            originY = _contentInsets.top + (currentItemOnPageIndex / _numberOfColumnsPerPage) * (lineSpacing + itemHeight);
        }
        
        // all attributes
        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        attributes.size = CGSizeMake(itemWidth, itemHeight);
        attributes.frame = CGRectMake(originX, originY, itemWidth, itemHeight);
        _layoutAttributes[indexPath] = attributes;
    }
    
    // content size
    NSUInteger pages = ceil(numberOfRows / (1.0 * numberOfItemsPerPage));
    _contentSize = CGSizeMake(collectionViewWidth * pages, collectionViewHeight);
}

- (CGSize)collectionViewContentSize {
    return _contentSize;
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attributes = [_layoutAttributes.allValues filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *  _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
        if (CGRectIntersectsRect(rect, evaluatedObject.frame)) {
            return YES;
        }
        return NO;
    }]];
    return attributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    return _layoutAttributes[indexPath];
}

@end
复制代码

###参考使用示例

  BICollectionViewFixedSizeLayout *_collectionViewFixedLayout = [[BICollectionViewFixedSizeLayout alloc] init];
 _collectionViewFixedLayout.contentInsets = UIEdgeInsetsMake(10, 10, 10, 10);
 _collectionViewFixedLayout.itemSize = CGSizeMake(36, 36);
 _collectionViewFixedLayout.numberOfLinesPerPage = 3;
_collectionViewFixedLayout.minimalColumnSpacing = 10;
_collectionViewFixedLayout.anchorLastItemPerPage = YES;
 [self.collectionView setCollectionViewLayout:_collectionViewFixedLayout animated:NO];
复制代码

到此,layout的代码已经写完,需要注意的是在使用anchorLastItemPerPage 时 numberOfItemsInSection需要在数据源的count基础上增加page页数,在使用时也要注意获取到正确的数据index来使用。 简单的应用场景,整理记录一下,后续可能会进行优化?。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值