之前寫git商城的時候看到類似需求自定義的item排布,這兩天有人又問到我具體怎么樣自定義layout來實現(xiàn),所以寫了demo來分享下實現(xiàn)的過程。
類似APP實現(xiàn)截圖
某商場截圖
demo實現(xiàn)效果圖
demo實現(xiàn)效果圖
下面具體分析實現(xiàn)步驟
1.首先初始化懶加載一個CollectionView,注冊Cell,Header,F(xiàn)ooter~
#pragma mark - LazyLoad
- (UICollectionView *)collectionView
{
if (!_collectionView) {
DCItemSortLayout *dcLayout = [DCItemSortLayout new];
dcLayout.delegate = self;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:dcLayout];
_collectionView.frame = self.view.bounds;
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.alwaysBounceVertical = YES;
[self.view addSubview:_collectionView];
[self.collectionView registerClass:[DCCollectionItemCell class] forCellWithReuseIdentifier:DCCollectionItemCellID]; //注冊cell
[self.collectionView registerClass:[DCHeaderReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:DCHeaderReusableViewID]; //注冊頭部
[self.collectionView registerClass:[DCFooterReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:DCFooterReusableViewID]; //注冊尾部
}
return _collectionView;
}
2.自定義DCItemSortLayout 在.h寫兩個代理方法,返回每組section頭部和尾部的高度
@protocol DCItemSortLayoutDelegate <NSObject>
@optional;
/* 頭部高度 */
-(CGFloat)dc_HeightOfSectionHeaderForIndexPath:(NSIndexPath *)indexPath;
/* 尾部高度 */
-(CGFloat)dc_HeightOfSectionFooterForIndexPath:(NSIndexPath *)indexPath;
@end
@interface DCItemSortLayout : UICollectionViewFlowLayout
@property (nonatomic, assign) id<DCItemSortLayoutDelegate>delegate;
3.在DCItemSortLayout .m文件中調(diào)用prepareLayout方法,聲明一個高度和可變字典
@property (nonatomic, assign) CGFloat overallHeight; //整體高
@property (nonatomic, strong) NSMutableArray *attrsArr; //布局?jǐn)?shù)組
/**
1.一個Cell對應(yīng)一個UICollectionViewLayoutAttributes對象
2.UICollectionViewLayoutAttributes對象決定了cell的擺設(shè)位置(frame)
*/
#pragma mark - 初始化屬性(section/item)
- (void)setUpAttributes
{
NSMutableArray *attributesArray = [NSMutableArray array];
NSInteger sectionCount = [self.collectionView numberOfSections];
for (int i = 0; i < sectionCount; i++) { //遍歷
//sectionHeader
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndex:i];
UICollectionViewLayoutAttributes *attrheader = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
[attributesArray addObject:attrheader];
//sectionItem
NSInteger itemCount = [self.collectionView numberOfItemsInSection:i];
for (int j = 0; j < itemCount; j++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:j inSection:i];
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[attributesArray addObject:attrs];
}
//sectionfooter
UICollectionViewLayoutAttributes *attrFooter = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter atIndexPath:indexPath];
[attributesArray addObject:attrFooter];
}
self.attrsArr = [NSMutableArray arrayWithArray:attributesArray];
}
#pragma mark - 對應(yīng)indexPath的位置的追加視圖的布局屬性
-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *layoutAttrbutes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:elementKind withIndexPath:indexPath];
CGFloat height = 0;
if (elementKind == UICollectionElementKindSectionHeader) { //頭部
if (_delegate != nil && [_delegate respondsToSelector:@selector(dc_HeightOfSectionHeaderForIndexPath:)]) {
height = [_delegate dc_HeightOfSectionHeaderForIndexPath:indexPath];
}
} else { //尾部
if (_delegate != nil && [_delegate respondsToSelector:@selector(dc_HeightOfSectionFooterForIndexPath:)]) {
height = [_delegate dc_HeightOfSectionFooterForIndexPath:indexPath];
}
}
layoutAttrbutes.frame = CGRectMake(0, self.overallHeight, ScreenW, height);
self.overallHeight += height;
return layoutAttrbutes;
}
#pragma mark - 返回rect中的所有的元素的布局屬性
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.attrsArr;
}
在我寫的demo中我自定義了兩組section
第一組
很明顯的可以看出 上面這一組中有4個item,代碼實現(xiàn)如下
#pragma mark - 自定義第一組section
- (void)layoutAttributesForCustomOneLayout:(UICollectionViewLayoutAttributes *)layoutAttributes indexPath: (NSIndexPath *) indexPath {
CGFloat itemY = self.overallHeight;
if (indexPath.item == 0) {
CGFloat itemH = 80;
layoutAttributes.frame = CGRectMake(0, itemY, ScreenW, itemH);
self.overallHeight += itemH;
} else {
NSInteger row = (indexPath.item -1) % 3;
CGFloat itemH = 100;
CGFloat itemW = ScreenW / 3;
layoutAttributes.frame = CGRectMake(row * itemW, itemY, itemW, itemH);
if (indexPath.item == 3 || indexPath.item == [self.collectionView numberOfItemsInSection:indexPath.section] - 1) {
self.overallHeight += itemH;
}
}
}
第二組
這一組中有3個item,代碼實現(xiàn)如下
#pragma mark - 自定義第二組section
- (void)layoutAttributesForCustomTwolayout:(UICollectionViewLayoutAttributes *)layoutAttributes indexPath: (NSIndexPath *) indexPath {
CGFloat itemY = self.overallHeight;
CGFloat itemW = ScreenW / 2;
CGFloat itemH = 160;
switch (indexPath.item) {
case 0:
layoutAttributes.frame = CGRectMake(0, itemY, itemW, itemH);
break;
case 1:
layoutAttributes.frame = CGRectMake(itemW, itemY, itemW, itemH/2);
break;
case 2:
layoutAttributes.frame = CGRectMake(itemW, (itemH/2) + itemY, itemW, itemH / 2.0);
break;
default:
break;
}
if (indexPath.item == [self.collectionView numberOfItemsInSection:indexPath.section] - 1) {
self.overallHeight += itemH;
}
}
這樣我們在自定義的UICollectionViewFlowLayout中已經(jīng)完成了對兩組Items的布局,接下來我們來到控制器界面~
數(shù)據(jù)源UICollectionViewDataSource
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 2;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return (section == 0) ? 4 : 3;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
DCCollectionItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DCCollectionItemCellID forIndexPath:indexPath];
cell.backgroundColor = [self RandomColor];
return cell;
}else{
DCCollectionItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DCCollectionItemCellID forIndexPath:indexPath];
cell.backgroundColor = [self RandomColor];
return cell;
}
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if (kind == UICollectionElementKindSectionHeader) {
DCHeaderReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:DCHeaderReusableViewID forIndexPath:indexPath];
headerView.headLabel.text = [NSString stringWithFormat:@"第%zd組頭部",indexPath.section];
return headerView;
} else if (kind == UICollectionElementKindSectionFooter) {
DCFooterReusableView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:DCFooterReusableViewID forIndexPath:indexPath];
footerView.backgroundColor = [UIColor lightGrayColor];
return footerView;
}
return [UICollectionReusableView new];
}
代理注意包括我們自定義的代理 DCItemSortLayoutDelegate,UICollectionViewDelegate
#pragma mark - UICollectionViewDelegate
#pragma mark - item點擊
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"點擊了第%zd組,第%zd個item",indexPath.section ,indexPath.row);
}
#pragma mark - DCItemSortLayoutDelegate
#pragma mark - 底部高度
-(CGFloat)dc_HeightOfSectionFooterForIndexPath:(NSIndexPath *)indexPath {
return 20;
}
#pragma mark - 頭部高度
-(CGFloat)dc_HeightOfSectionHeaderForIndexPath:(NSIndexPath *)indexPath {
return 50;
}
- 這樣自定義layout就完成了,之前也參考過幾個git上幾個哥們的布局對于自定義layout 的理解,很有收獲非常感謝~
- 如上實現(xiàn)源碼基本已全部展示,如果有需要我寫的demo,可以給我留言私法~