自定義UICollectionViewLayout實現瀑布流
阿新 • • 發佈:2019-02-01
該瀑布流的列數,列與列,行與行,距離四周的間距均通過代理由外界傳入
// // ViewController.m // PuBuFlow // // Created by hq on 16/5/11. // Copyright © 2016年 hanqing. All rights reserved. // #import "ViewController.h" #import "HQFlowLayout.h" #import "HQCollectionViewCell.h" #import <MJRefresh.h> #import <MJExtension.h> #import "HQShop.h" @interface ViewController () <UICollectionViewDataSource,HQFlowLayoutDelegate> @property(nonatomic,strong) NSMutableArray *goodsArrays; @property(nonatomic,weak) UICollectionView *collectionView; @end static NSString * const
[email protected]"flow_cell"; @implementation ViewController -(NSMutableArray *)goodsArrays{ if (_goodsArrays==nil) { _goodsArrays=[NSMutableArray array]; } return _goodsArrays; } - (void)viewDidLoad { [super viewDidLoad]; [self setUpCollection]; [self setUpRefresh]; } -(void) setUpCollection{ HQFlowLayout *flow=[[HQFlowLayout alloc]init]; flow.delegate=self; UICollectionView *collectionView=[[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:flow]; collectionView.backgroundColor=[UIColor whiteColor]; collectionView.dataSource=self; [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([HQCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:cellID]; [self.view addSubview:collectionView]; self.collectionView=collectionView; } -(void) setUpRefresh{ //建立下拉重新整理 self.collectionView.mj_header=[MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; [self.collectionView.mj_header beginRefreshing]; //建立上拉重新整理 self.collectionView.mj_footer=[MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; } -(void) loadNewData{ [self.goodsArrays removeAllObjects]; NSMutableArray *array=[HQShop mj_objectArrayWithFilename:@"1.plist"]; [self.goodsArrays addObjectsFromArray:array]; [self.collectionView reloadData]; [self.collectionView.mj_header endRefreshing]; } -(void) loadMoreData{ NSMutableArray *array=[HQShop mj_objectArrayWithFilename:@"1.plist"]; [self.goodsArrays addObjectsFromArray:array]; [self.collectionView reloadData]; [self.collectionView.mj_footer endRefreshing]; } -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.goodsArrays.count; } -(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ HQShop *shop=self.goodsArrays[indexPath.item]; HQCollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath]; cell.shop=shop; return cell; } #pragma mark 我們自定義的瀑布流HQFlowLayout當中的代理實現方法 //設定每個cell的高度 -(CGFloat)flowLayout:(HQFlowLayout *)flowLayout heightForItemAtIndex:(NSInteger)index itemWidth:(CGFloat)itemWidth{ HQShop *shop=self.goodsArrays[index]; //返回高度 return shop.h*itemWidth/shop.w; } //設定cell的列數 -(CGFloat)columNumbersFlowLayout:(HQFlowLayout *)flowLayout{ return 3; } //設定行與行之間的間距 -(CGFloat)rowMarginFlowLayout:(HQFlowLayout *)flowLayout{ return 10; } //設定列與列之間的間距 -(CGFloat)colMarginFlowLayout:(HQFlowLayout *)flowLayout{ return 10; } //設定四周邊緣的距離 -(UIEdgeInsets)edgeIndsetsFlowLayout:(HQFlowLayout *)flowLayout{ UIEdgeInsets sets=UIEdgeInsetsMake(10, 10, 10, 10); return sets; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end
核心方法如下
// // HQFlow.h // PuBuFlow // // Created by hq on 16/5/11. // Copyright © 2016年 hanqing. All rights reserved. // #import <UIKit/UIKit.h> @class HQFlowLayout; @protocol HQFlowLayoutDelegate <NSObject> @required //返回每行的高度 -(CGFloat) flowLayout:(HQFlowLayout *) flowLayout heightForItemAtIndex:(NSInteger) index itemWidth:(CGFloat) itemWidth; @optional //返回列數 -(CGFloat) columNumbersFlowLayout:(HQFlowLayout *) flowLayout; //返回行與行之間的間距 -(CGFloat) rowMarginFlowLayout:(HQFlowLayout *) flowLayout; //返回列與列之間的間距 -(CGFloat) colMarginFlowLayout:(HQFlowLayout *) flowLayout; //設定距離邊緣四周的距離 -(UIEdgeInsets) edgeIndsetsFlowLayout:(HQFlowLayout *) flowLayout; @end @interface HQFlowLayout : UICollectionViewLayout @property(nonatomic,weak) id<HQFlowLayoutDelegate> delegate; @end
//
// HQFlow.m
// PuBuFlow
//
// Created by hq on 16/5/11.
// Copyright © 2016年 hanqing. All rights reserved.
//
#import "HQFlowLayout.h"
#import <MJExtension.h>
#import <MJRefresh.h>
@interface HQFlowLayout()
@property(nonatomic,strong) NSMutableArray *dataArrays;
@property(nonatomic,strong) NSMutableArray *colHeightArrays;
@end
@implementation HQFlowLayout
//有3列
static NSInteger colNumber=3;
//每行之間的間距
static CGFloat rowMargin=10;
//每列之間的間距
static CGFloat colMargin=10;
//距離邊緣的間距
static UIEdgeInsets boderInsets={10,10,10,10};
#pragma mark 懶載入
-(NSMutableArray *)dataArrays{
if (_dataArrays==nil) {
_dataArrays=[NSMutableArray array];
}
return _dataArrays;
}
//儲存每行的高度
-(NSMutableArray *)colHeightArrays{
if (_colHeightArrays==nil) {
_colHeightArrays=[NSMutableArray array];
}
return _colHeightArrays;
}
#pragma mark 通過代理獲取屬性,獲取不到,則使用預設值
//獲取列數
-(NSInteger) getColumNumber{
if ([self.delegate respondsToSelector:@selector(columNumbersFlowLayout:)]) {
return [self.delegate columNumbersFlowLayout:self];
}
//沒有設定則用預設值
return colNumber;
}
//獲取行與行之間的間距
-(CGFloat) getRowMargin{
if ([self.delegate respondsToSelector:@selector(rowMarginFlowLayout:)]) {
return [self.delegate rowMarginFlowLayout:self];
}
//沒有設定則用預設值
return rowMargin;
}
//獲取列與列之間的間距
-(CGFloat) getColMargin{
if ([self.delegate respondsToSelector:@selector(colMarginFlowLayout:)]) {
return [self.delegate colMarginFlowLayout:self];
}
//沒有設定則用預設值
return colMargin;
}
-(UIEdgeInsets) getEdgeInsets{
if ([self.delegate respondsToSelector:@selector(edgeIndsetsFlowLayout:)]) {
return [self.delegate edgeIndsetsFlowLayout:self];
}
//沒有設定則用預設值
return boderInsets;
}
//初始化操作,必須寫super prepareLayout
//每次表格reloaddata,都會呼叫該方法
-(void)prepareLayout{
[self.dataArrays removeAllObjects];
[self.colHeightArrays removeAllObjects];
[super prepareLayout];
NSLog(@"%s",__func__);
//初始化我們每個col的高度,預設為距離頂部的高度
for (int i=0; i<[self getColumNumber]; i++) {
[self.colHeightArrays addObject:@([self getEdgeInsets].top)];
}
NSInteger cellCount=[self.collectionView numberOfItemsInSection:0];
//初始化我們所有cell屬性的陣列
for (int i=0; i<cellCount; i++) {
NSIndexPath *indexpath=[NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attr=[self layoutAttributesForItemAtIndexPath:indexpath];
[self.dataArrays addObject:attr];
}
}
//所有元素的屬性
//該方法,每滾動一次就會被呼叫一次,因此我們把陣列的初始化放到preparelayout當中去
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.dataArrays;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *attr=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGFloat w=(self.collectionView.bounds.size.width-[self getColMargin]*([self getColumNumber]-1)-[self getEdgeInsets].left-[self getEdgeInsets].right)/[self getColumNumber];
// CGFloat h=arc4random_uniform(100)+80;
//通過代理方法獲取高度
CGFloat h=[self.delegate flowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
//接下來計算哪個列最短
CGFloat minHeight=[self.colHeightArrays[0] doubleValue];
NSInteger minCol=0;
for (int i=1; i<[self getColumNumber]; i++) {
CGFloat currentHeight=[self.colHeightArrays[i] doubleValue];
if (currentHeight<minHeight) {
minCol=i;
minHeight=currentHeight;
}
}
CGFloat x=[self getEdgeInsets].left+(w+[self getColMargin])*minCol;
CGFloat y=minHeight+[self getRowMargin];
attr.frame=CGRectMake(x, y, w, h);
self.colHeightArrays[minCol][email protected](CGRectGetMaxY(attr.frame));
return attr;
}
-(CGSize)collectionViewContentSize{
//取出我們的最大高度
CGFloat maxHeight=[self.colHeightArrays[0] doubleValue];
for (int i=1; i<[self getColumNumber]; i++) {
CGFloat currentHeight=[self.colHeightArrays[i] doubleValue];
if (currentHeight>maxHeight) {
maxHeight=currentHeight;
}
}
return CGSizeMake(0, maxHeight);
}
@end