自定义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;

@end

@interface WaterFlowLayout : UICollectionViewLayout

//瀑布流一共多少列
@property (nonatomic, assign) NSUInteger numberOfColumn;

@property (nonatomic, assign) id<WaterFlowLayoutDelegate>delegate;

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

@interface WaterFlowLayout ()

//存放每一列的高度
@property (nonatomic, retain) NSMutableArray *columnHeightsArray;

//存放 每一个item的 属性 包含 frame以及下标
@property (nonatomic, retain) NSMutableArray *attributesArray;

@end

@implementation 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的信息重新告诉集合视图,进行显示。

时间: 2024-08-08 01:05:33

自定义UICollectinviewFlowLayout,即实现瀑布流的相关文章

iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流

上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewController(二) --详解CollectionView各种回调>.UICollectionView之所以强大,是因为其具有自定义功能,这一自定义就不得了啦,自由度非常大,定制的高,所以功能也是灰常强大的.本篇博客就不使用自带的流式布局了,我们要自定义一个瀑布流.自定义的瀑布流可以配置其参数: 每个Cell的边距

自定义实现带文字标题的瀑布流效果

在网上能找到的大部分资料中的瀑布流效果都是单纯的照片实现,现在我来实现一个带文字标题的.效果如下: 每个item都是由图片和文字标题两部分组成. 布局方式为ScrollView里面嵌套一个水平方向的LinearLayout,里面再嵌套两个竖直方向的LinearLayout,然后判断竖直方向的两个LinearLayout的高度,向比较低的那个Linearlayout里面添加item. 下面是代码 <ScrollView xmlns:android="http://schemas.androi

自定义UICollectionLayout布局 —— UIKit之学习UICollectionView记录一《瀑布流》

一.思路 思路一:比较每一行所有列的cell的高度,从上到下(也就是从第一行开始),从最短的开始计算,(记录下b的高度和索引,从开始计算,依次类推) 思路二:设置上.下.左.右间距和行间距.列间距及列数. 思路三:实现的重要的方法. 二.代码先行. 1.自定义layout类. //入口 #import <UIKit/UIKit.h> @protocol STRWaterLayoutDelegate; @interface STRWaterLayout : UICollectionViewLay

自定义UICollectionViewLayout之瀑布流

目标效果 因为系统给我们提供的 UICollectionViewFlowLayout 布局类不能实现瀑布流的效果,如果我们想实现 瀑布流 的效果,需要自定义一个 UICollectionViewLayout  类,实现瀑布流效果.效果如右图. 依赖工具: 我们需要一个图片大小和图片地址的Josn数据, 和 SDWebImage图片加载的第三方工具 RootViewController.m 1 #import "RootViewController.h" 2 #import "

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作) 一.简单说明 使用数据刷新框架: 该框架提供了两种刷新的方法,一个是使用block回调(存在循环引用问题,_ _weak),一个是使用调用. 问题:在进行下拉刷新之前,应该要清空之前的所有数据(在刷新数据这个方法中). 移除正在显示的cell: (1)把字典中的所有的值,都从屏幕上移除 (2)清除字典中的所有元素 (3)清除cell的frame,每个位置的cell的frame都要重新计算 (4)清除可复用的缓存池. 该部分的代码如下: 1

iOS开发之窥探UICollectionViewController(四) --一款功能强大的自定义瀑布流

在上一篇博客中<iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流>,自定义瀑布流的列数,Cell的外边距,Cell的最大以及最小高度是在我们的布局文件中是写死的,换句话说也就是不可配置的.为了循序渐进,由浅入深呢,上篇博客暂且那么写.不过那样写太过死板,本来使用起来比较灵活的自定义布局,如果把其配置参数给写死了,就相当于在笼中的猛兽,再厉害不也白扯蛮. 在今天这篇博客中我们要接着上篇博客中的Demo,使其自定义布局

iOS开发UI篇—自定义瀑布流控件(基本实现)

iOS开发UI篇—自定义瀑布流控件(基本实现) 一.基本实现 说明:在View加载的时候,刷新数据. 1.实现代码 YYViewController.m文件 1 // 2 // YYViewController.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-28. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYViewControll

iOS开发UI篇—自定义瀑布流控件(蘑菇街实现)

iOS开发UI篇—自定义瀑布流控件(蘑菇街瀑布流) 一.简单说明 关于瀑布流 1.是使用UIScrollView实现的 2.刷新数据(reloadData)方法里面做哪些事情 3.layoutSubviews方法里面做哪些事情 4.模仿UItableView进行设计 完善: 瀑布流控件第一次显示到屏幕上的时候自动的向数据源索要数据,而不需要手动调用.这需要监听View的显示,View的显示有一个方法,叫做willMoveToSuperview:在该方法中直接刷新一次数据即可. 二.把自定义的瀑布

瀑布流的简单自定义

#import <UIKit/UIKit.h> @class UICollectionViewFlowWaterLayout; @protocol UICollectionViewFlowWaterDelegate <UICollectionViewDelegate> - (CGFloat)collectionView:(UICollectionView *)c layout:(UICollectionViewFlowWaterLayout *)collectionViewLayo