zoukankan      html  css  js  c++  java
  • iOS之简单瀑布流的实现

    iOS之简单瀑布流的实现

     

    前言

    超简单的瀑布流实现,这里说一下笔者的思路,详细代码在这里

    实现思路

    collectionView能实现各中吊炸天的布局,其精髓就在于UICollectionViewLayout,因此我们要自定义一个layout来继承系统的UICollectionViewLayout,所有工作都在这个类中进行。

    1.定义所需属性

    瀑布流的思路就是,从上往下,那一列最短,就把下一个item放在哪一列,因此我们需要定义一个字典来记录每一列的最大y值

    每一个item都有一个attributes,因此定义一个数组来保存每一个item的attributes。

    我们还必须知道有多少列以及列间距、行间距、section到collectionView的边距。

    //总列数
    @property (nonatomic, assign) NSInteger columnCount;
    //列间距
    @property (nonatomic, assign) NSInteger columnSpacing;
    //行间距
    @property (nonatomic, assign) NSInteger rowSpacing;
    //section到collectionView的边距
    @property (nonatomic, assign) UIEdgeInsets sectionInset;
    //保存每一列最大y值的数组
    @property (nonatomic, strong) NSMutableDictionary *maxYDic;
    //保存每一个item的attributes的数组
    @property (nonatomic, strong) NSMutableArray *attributesArray;

    2.重写系统方法

    我们一共需要重写4个方法

    1)- (void)prepareLayout
    2)- (CGSize)collectionViewContentSize
    3)- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    4)- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

    - (void)prepareLayout 方法

    布局前的一些准备工作都在这里进行,初始化字典,有几列就有几个键值对,key为第几列,value为列的最大y值,初始值为上内边距:

    for (int i = 0; i < self.columnCount; i++) {
        self.maxYDic[@(i)] = @(self.sectionInset.top);
    }

    创建每个item的attributes,并存入数组:

    //根据collectionView获取总共有多少个item
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    //为每一个item创建一个attributes并存入数组
    for (int i = 0; i < itemCount; i++) {
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        [self.attributesArray addObject:attributes];
    }

    - (CGSize)collectionViewContentSize 方法

    用来计算collectionView的contentSize

    一般瀑布流只能垂直滚动,不能水平滚动,因此contentSize.width = 0,我们只需要计算contentSize.height即可

    从字典中找出最长列的最大y值,再加上下面的内边距,即为contentSize.height

    - (CGSize)collectionViewContentSize {
        //假设第0列是最长的那列
        __block NSNumber *maxIndex = @0;
        //遍历字典,找出最长的那一列
        [self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
            //如果maxColumn列的最大y值小于obj,则让maxColumn等于obj所属的列
            if ([self.maxYDic[maxIndex] floatValue] < obj.floatValue) {
                maxIndex = key;
            }
        }];
        //collectionView的contentSize.height就等于最长列的最大y值+下内边距
        return CGSizeMake(0, [self.maxYDic[maxIndex] floatValue] + self.sectionInset.bottom);
    }

    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 方法

    该方法则用来设置每个item的attributes,在这里,我们只需要简单的设置每个item的attributes.frame即可

    首先我们必须得知collectionView的尺寸,然后我们根据collectionView的宽度,以及列数、各个间距来计算每个item的宽度

    item的宽度 = (collectionView的宽度 - 内边距及列边距) / 列数

    CGFloat collectionViewWidth = self.collectionView.frame.size.width;
    //self.sectionInset.left:左边距    self.sectionInset.right:右边距
    //(self.columnCount - 1) * columnSpacing:一行中所有的列边距
    CGFloat itemWidth = (collectionViewWidth - self.sectionInset.left - self.sectionInset.right - (self.columnCount - 1) * self.columnSpacing) / self.columnCount;

    接下来计算item的坐标,要想计算坐标,那就必须知道最短的那一列,先遍历字典,找出最短列是哪一列(minColumn)以及其最大y值。

    item的y值就等于最短列的最大y值再加上行间距,x值就等于左边距 + (item宽度 + 列间距) * minColumn

    //找出最短的那一列
    __block NSNumber *minIndex = @0;
    [self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
        if ([self.maxYDic[minIndex] floatValue] > obj.floatValue) {
            minIndex = key;
        }
    }];
    //根据最短列的列数计算item的x值
    CGFloat itemX = self.sectionInset.left + (self.columnSpacing + itemWidth) * minIndex.integerValue;
    //item的y值 = 最短列的最大y值 + 行间距
    CGFloat itemY = [self.maxYDic[minIndex] floatValue] + self.rowSpacing;

    接下来便是item的高度,我们应该根据图片的原始尺寸以及计算出来的宽度,等比例缩放来计算高度,但是在layout类中,我们是拿不到图片的,因此我们可以定义一个block属性,或者代理,让外界来计算并返回给我们,我们需要将item的宽度以及indexPath传递给外界:

    @property (nonatomic, strong) CGFloat(^itemHeightBlock)(CGFloat itemHeight,NSIndexPath *indexPath);

    根据返回值来设置item的高度:

    if (self.itemHeightBlock) itemHeight = self.itemHeightBlock(itemWidth, indexPath);

    最后设置attributes的frame并更新字典:

    //设置attributes的frame
    attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemHeight);
    //更新字典中的最短列的最大y值
    self.maxYDic[minIndex] = @(CGRectGetMaxY(attributes.frame));

    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 方法

    该方法用来返回rect范围内,item的attributes

    直接返回attributesArray即可

    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    return self.attributesArray;
    }

    使用

    布局类写完了,接下来就可以直接使用了

    //创建布局对象
    XRWaterfallLayout *waterfall = [[XRWaterfallLayout alloc] init];
    //设置相关属性
    waterfall.columnCount = 3;//共多少列
    waterfall.columnSpacing = 10;//列间距
    waterfall.rowSpacing = 10;//行间距
    waterfall.sectionInset = UIEdgeInsetsMake(10, 10 , 10, 10);//内边距
    [waterfall setItemHeightBlock:^CGFloat(CGFloat itemWidth, NSIndexPath *indexPath) {
    //根据图片的原始尺寸,及显示宽度,等比例缩放来计算显示高度
    XRImage *image = self.images[indexPath.item];
    return image.imageH / image.imageW * itemWidth;
    }];
    collectionView.collectionViewLayout = waterfall;
    ......
    ......

    具体代码请到这里下载:https://github.com/codingZero/XRWaterfallLayout,觉得不错的,请献上你的star

  • 相关阅读:
    虚拟机VMware配置centos7集群(亲测有效)
    linux虚拟机克隆后,虚拟机ping不通的解决方法
    VC++使用 GDI+等比例缩放图片,并且居中显示
    VS2015 编译OSG Plugins Giflib静态库
    Qt 读写文件操作
    OSG 常用快捷键(全屏、查看帧数、截屏)
    Navicat Premium v15 中文最新破解版(附:激活工具)
    redis 持久化机制及配置
    Redis 五种数据类型
    linux 安装redis
  • 原文地址:https://www.cnblogs.com/LiLihongqiang/p/5922442.html
Copyright © 2011-2022 走看看