你的位置:首页 > 操作系统

[操作系统]自定义UICollectinviewFlowLayout,即实现瀑布流

如图所示,通过实现不规则的网格分布,来显示出不同的效果。因为集合视图必须要指定布局还可以显示,所以自定义布局就可以实现瀑布流的效果。

//创建布局对象  WaterFlowLayout *flowLayout = [[WaterFlowLayout alloc] init];    flowLayout.delegate = self;  flowLayout.numberOfColumn = 3;    //创建集合视图  UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:flowLayout];

因为系统自带的布局有四个方法,分别实现了item大小,分区间隔,最小行间距,item之间的间隙大小

@protocol UICollectionViewDelegateFlowLayout <UICollectionViewDelegate>@optional- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;

所以,自定义FlowLayout,并定义协议,以便定义这些方法。

@protocol WaterFlowLayoutDelegate <NSObject>//关键方法,此方法的作用是返回每一个item的size大小//数据中原始图片大小- (CGSize)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;//分区间隔- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;//得到 item之间的间隙大小- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;//最小行间距- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;@[email protected] WaterFlowLayout : [email protected] (nonatomic, assign) NSUInteger numberOfColumn;@property (nonatomic, assign) id<WaterFlowLayoutDelegate>delegate;

看图可知,最小高的的item,将作为下一列item的起点。

@interface WaterFlowLayout ()[email protected] (nonatomic, retain) NSMutableArray *columnHeightsArray;//存放 每一个item的 属性 包含 [email protected] (nonatomic, retain) NSMutableArray *attributesArray;@[email protected] WaterFlowLayout//获取最小高度的方法- (CGFloat)minHeight{  CGFloat min = 100000;  for (NSNumber *height in _columnHeightsArray) {    CGFloat h = [height floatValue];    if (min > h) {      min = h;    }  }  return min;}//获取最大值- (CGFloat)maxHeight{  CGFloat max = 0;  for (NSNumber *height in _columnHeightsArray) {    CGFloat h = [height floatValue];    if (max < h) {      max = h;    }  }  return max;}//找到最小高的下标- (NSUInteger)indexOfMinHeight{  NSUInteger index = 0;  for (int i = 0; i < [_columnHeightsArray count]; i ++) {    CGFloat height = [_columnHeightsArray[i] floatValue];    if (height == [self minHeight]) {      index = i;      return index;    }  }  return index;}//重写父类的布局方法- (void)prepareLayout{  [super prepareLayout];    _attributesArray = [[NSMutableArray alloc] init];    _columnHeightsArray = [[NSMutableArray alloc] initWithCapacity:self.numberOfColumn];    //给列高数组里面的对象赋初值  for (int i = 0; i < self.numberOfColumn; i ++) {    [_columnHeightsArray addObject:@0.0];  }    CGFloat totalWidth = self.collectionView.frame.size.width;    //创建 每个item frame中的x、y  CGFloat x = 0;  CGFloat y = 0;    NSUInteger itemCount = [self.collectionView numberOfItemsInSection:0];    for (int i = 0; i < itemCount; i ++) {    //得到集合视图中 列间隙的个数    NSUInteger numberOfSpace = self.numberOfColumn - 1;        //代理对象执行代理方法,得到 item之间的间隙大小    CGFloat spaceWidth = [_delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:0];        //求每列的宽度,也就是每个item的width    CGFloat width = (totalWidth - spaceWidth * numberOfSpace) / self.numberOfColumn;            //获取每一个itemSize的大小    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];        //数据中原始图片大小    CGSize imageSize = [_delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];      //通过 约分公式得到固定宽之后的高度是多少    CGFloat height = width * imageSize.height / imageSize.width;            UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];        //记录每一个item的大小和位置    attribute.frame = CGRectMake(x, y, width, height);        //数组保存每个item的位置信息    [_attributesArray addObject:attribute];        NSLog(@"item = %d",i);    NSLog(@"x = %.2f y = %.2f width = %.2f height = %.2f",x,y,width,height);        //求列高最小的那一列的下标    NSUInteger minHeightIndex = [self indexOfMinHeight];        //求出最小列的高度    CGFloat minHeight = [_columnHeightsArray[minHeightIndex] floatValue];        //求出行高    CGFloat lineHeight = [_delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:0];        //上一次总的列高 加上 行高 加上新加上的item的height,才是现在这一列的总高度    //minHeight为最小列现在的高度    //lineHeight为行间距    //height为新加的item的高    _columnHeightsArray[minHeightIndex] = [NSNumber numberWithFloat:minHeight + lineHeight + height];        //重新算最小列高的下标    minHeightIndex = [self indexOfMinHeight];        //算下一次新加的item的x和y值    x = (spaceWidth + width) * minHeightIndex;        y = [self minHeight];  }}//重写这个方法,可以返回集合视图的总高度- (CGSize)collectionViewContentSize{  return CGSizeMake(self.collectionView.frame.size.width, [self maxHeight]);}- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{  return _attributesArray;}

注意,最后一个方法的实现,即- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect,如果这个方法不写,集合视图是显示不出来的,这个方法是次保存的每个item的信息重新告诉集合视图,进行显示。