需求:emoji的橫向顯示,在每頁的最後需要顯示一個刪除按鈕,如下圖所示。
- 可以先閱讀下蘋果的文件 必須需要覆蓋的方法 collectionViewContentSize layoutAttributesForElements(in:) layoutAttributesForItem(at:) layoutAttributesForSupplementaryView(ofKind:at:) (如果您的佈局支援補充檢視) layoutAttributesForDecorationView(ofKind:at:) (如果您的佈局支援裝飾檢視)
當然還有當資料更改移動刪除時需要覆蓋的方法,目前沒有此需求,先不談,具體可以看文件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來使用。 簡單的應用場景,整理記錄一下,後續可能會進行優化?。