• iOS GZWaterfall任何形式的瀑布流


    概述

    使用UICollectionView可以布局各种各样的瀑布流,下面我写了几种不同布局的瀑布流样式

    详细

    首先,将所有的类型展示给大家;

    屏幕快照 2017-08-09 14.42.22.png

    上面图片中展示的样式在Demo中都有实现。

    一、项目结构

    屏幕快照 2017-08-08 10.15.13.png

    对于我们要实现的各种各样的 collectionView,根据不同的需求设置不同的列数 ,列边距,行边距,collectionView边距

    二、程序实现

    1、随机瀑布流

    屏幕快照 2017-08-09 15.48.30.png

    #pragma mark - 创建collectionView
    - (void)setupCollectionView
    {
        GZFallsLayout *fallsLayout = [[GZFallsLayout alloc] init];
        fallsLayout.delegate = self;
        UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:fallsLayout];
        [self.view addSubview:collectionView];
        _collectionView = collectionView;
        collectionView.dataSource = self;
        [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([GZShopCell class]) bundle:nil] forCellWithReuseIdentifier:ID];
    }
    
    #pragma mark - 创建上下拉刷新
    - (void)setupRefresh
    {
        self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewShops)];
        self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreShops)];
        self.collectionView.backgroundColor = [UIColor whiteColor];
        [self.collectionView.mj_header beginRefreshing];
    }
    
    #pragma mark - 加载下拉数据
    - (void)loadNewShops
    {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSArray *shops = [GZShop mj_objectArrayWithFilename:@"1.plist"];
            [weakSelf.shops removeAllObjects];
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.collectionView reloadData];
                [weakSelf.shops addObjectsFromArray:shops];
                [weakSelf.collectionView.mj_header endRefreshing];
                [weakSelf.collectionView reloadData];
            });
        });
    }
    
    #pragma mark - 加载上拉数据
    - (void)loadMoreShops
    {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSArray *shops = [GZShop mj_objectArrayWithFilename:@"1.plist"];
            [weakSelf.shops addObjectsFromArray:shops];
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.collectionView.mj_footer endRefreshing];
                [weakSelf.collectionView reloadData];
            });
        });
    }
    
    - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
    {
        return 1;
    }
    
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
        self.collectionView.mj_footer.hidden = self.shops.count == 0;
        return self.shops.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        GZShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
        if (self.shops && self.shops.count >= indexPath.item+1) cell.shop = self.shops[indexPath.item];
        return cell;
    }
    
    - (CGFloat)columnMarginInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return 5;
    }
    
    - (CGFloat)rowMarginInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return 5;
    }
    
    - (CGFloat)columnCountInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return 4;
    }
    
    - (UIEdgeInsets)edgeInsetsInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return UIEdgeInsetsMake(0, 10, 20, 10);
    }
    
    - (NSMutableArray *)shops
    {
        if (!_shops) {
            _shops = [NSMutableArray array];
        }
        return _shops;
    }
    
    // 计算布局属性
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        // 每个collectionView的宽度
        CGFloat collectionViewW = self.collectionView.frame.size.width;
        // 每个cell的宽度
        CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right -
                     self.columnMargin * (self.columnCount - 1)) / self.columnCount;
        // cell的高度
        NSUInteger randomOfHeight = arc4random() % 100;
        CGFloat h = w * (randomOfHeight >= 50 ? 250 : 320) / 200;
        
        // cell应该拼接的列数
        NSInteger destColumn = 0;
        
        // 高度最小的列数高度
        CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
        // 获取高度最小的列数
        for (NSInteger i = 1; i < self.columnCount; i++) {
            CGFloat columnHeight = [self.columnHeights[i] doubleValue];
            if (minColumnHeight > columnHeight) {
                minColumnHeight = columnHeight;
                destColumn = i;
            }
        }
        
        // 计算cell的x
        CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
        // 计算cell的y
        CGFloat y = minColumnHeight;
        if (y != self.edgeInsets.top) {
            y += self.rowMargin;
        }
        
        // 随机数,用来随机生成大尺寸cell
        NSUInteger randomOfWhetherDouble = arc4random() % 100;
        // 判断是否放大
        if (destColumn < self.columnCount - 1                               // 放大的列数不能是最后一列(最后一列方法超出屏幕)
            && _noneDoubleTime >= 1                                         // 如果前个cell有放大就不放大,防止连续出现两个放大
            && (randomOfWhetherDouble >= 45 || _noneDoubleTime >= 8)        // 45%几率可能放大,如果累计8次没有放大,那么满足放大条件就放大
            && [self.columnHeights[destColumn] doubleValue] == [self.columnHeights[destColumn + 1] doubleValue] // 当前列的顶部和下一列的顶部要对齐
            && _lastDoubleIndex != destColumn) {             // 最后一次放大的列不等当前列,防止出现连续两列出现放大不美观
            _noneDoubleTime = 0;
            _lastDoubleIndex = destColumn;
            // 重定义当前cell的布局:宽度*2,高度*2
            attrs.frame = CGRectMake(x, y, w * 2 + self.columnMargin, h * 2 + self.rowMargin);
            // 当前cell列的高度就是当前cell的最大Y值
            self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
            // 当前cell列下一列的高度也是当前cell的最大Y值,因为cell宽度*2,占两列
            self.columnHeights[destColumn + 1] = @(CGRectGetMaxY(attrs.frame));
        } else {
            // 正常cell的布局
            if (_noneDoubleTime <= 3 || _lastFixIndex == destColumn) {                     // 如果没有放大次数小于3且当前列等于上次矫正的列,就不矫正
                attrs.frame = CGRectMake(x, y, w, h);
            } else if (self.columnHeights.count > destColumn + 1                         // 越界判断
                && y + h - [self.columnHeights[destColumn + 1] doubleValue] < w * 0.1) { // 当前cell填充后和上一列的高度偏差不超过cell最大高度的10%,就和下一列对齐
                attrs.frame = CGRectMake(x, y, w, [self.columnHeights[destColumn + 1] doubleValue] - y);
                _lastFixIndex = destColumn;
            } else if (destColumn >= 1                                                   // 越界判断
                       && y + h - [self.columnHeights[destColumn - 1] doubleValue] < w * 0.1) { // 当前cell填充后和上上列的高度偏差不超过cell最大高度的10%,就和下一列对齐
                attrs.frame = CGRectMake(x, y, w, [self.columnHeights[destColumn - 1] doubleValue] - y);
                _lastFixIndex = destColumn;
            } else {
                attrs.frame = CGRectMake(x, y, w, h);
            }
            // 当前cell列的高度就是当前cell的最大Y值
            self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
            _noneDoubleTime += 1;
        }
        // 返回计算获取的布局
        return attrs;
    }

    2、规则瀑布流

    屏幕快照 2017-08-09 15.56.19.png

    // 计算布局属性
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        if (indexPath.item == 0) {
            attrs.frame = CGRectMake(0, 0, ([UIScreen mainScreen].bounds.size.width - 4)/3*2 +2, ([UIScreen mainScreen].bounds.size.width - 4)/3*2 +2);
        }else if (indexPath.item == 1){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3*2 +4, 0, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else if (indexPath.item == 2){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3*2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3 +2, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else if (indexPath.item == 3){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3*2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3 *2 + 4, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else if (indexPath.item == 4){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3 +2, ([UIScreen mainScreen].bounds.size.width - 4)/3 *2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else{
            attrs.frame = CGRectMake(0, ([UIScreen mainScreen].bounds.size.width - 4)/3 *2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }
        // 返回计算获取的布局
        return attrs;
    }

    3、简单两排瀑布流

    屏幕快照 2017-08-09 15.58.30.png

    -(CGSize)collectionViewContentSize
    {
        //计算整个contentsize的大小
        __block CGFloat height=0;
        [arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if([obj floatValue]>height)
            {
                height=[obj floatValue];
            }
        }];
        return CGSizeMake(self.collectionView.bounds.size.width, height);
    }
    
    -(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        //计算每一个item的相关属性
        UICollectionViewLayoutAttributes *attr=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        int index=0;
        if([arr[0] floatValue]<[arr[1] floatValue])
        {
            index=0;
        }
        else
        {
            index=1;
        }
        CGFloat width=self.collectionView.bounds.size.width/2;
        CGFloat height=arc4random()%200+100;
        CGFloat left=index*width;
        CGFloat top=[arr[index] floatValue];
        CGRect frame=CGRectMake(left, top, width, height);
        attr.frame=frame;
        arr[index]=@([arr[index] floatValue]+height);
        return attr;
    }

    4、两排瀑布流

    屏幕快照 2017-08-09 15.59.10.png

    -(void)prepareLayout{
    
        _attributeArray = [NSMutableArray array];
        [super prepareLayout];
        float WIDTH = ([UIScreen mainScreen].bounds.size.width-self.sectionInset.left-self.sectionInset.right-self.minimumInteritemSpacing)/2;
        CGFloat colHight[2] = {self.sectionInset.top,self.sectionInset.bottom};
        for (int i=0; i<_itemCount; i++) {
            
            NSIndexPath * index = [NSIndexPath indexPathForItem:i inSection:0];
            UICollectionViewLayoutAttributes * attribute =  [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
            CGFloat height = arc4random()%150+40;
            int width = 0;
            if (colHight[0]<colHight[1]) {
                colHight[0] = colHight[0]+height+self.minimumLineSpacing;
                width = 0; 
            }else{
                colHight[1] = colHight[1]+height+self.minimumLineSpacing;
                width = 1;
            }
            attribute.frame = CGRectMake(self.sectionInset.left+(self.minimumInteritemSpacing+WIDTH)*width, colHight[width]-height-self.minimumLineSpacing, WIDTH, height);
            [_attributeArray addObject:attribute];
            
        }
        if (colHight[0]>colHight[1]) {
            self.itemSize = CGSizeMake(WIDTH, (colHight[0]-self.sectionInset.top)*2/_itemCount-self.minimumLineSpacing);
        }else{
            self.itemSize = CGSizeMake(WIDTH, (colHight[1]-self.sectionInset.top)*2/_itemCount-self.minimumLineSpacing);
        }
    
    }

    5、环形瀑布流

    屏幕快照 2017-08-09 16.00.22.png

     _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
        _attributeArray = [NSMutableArray array];
        CGFloat radius  =MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height)/2;
        CGPoint center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2);
        for (int i=0; i<_itemCount; i++) {
            UICollectionViewLayoutAttributes * attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
            attris.size = CGSizeMake(50, 50);
            float x = center.x+cosf(2*M_PI/_itemCount*i)*(radius-25);
            float y = center.y+sinf(2*M_PI/_itemCount*i)*(radius-25);
            attris.center = CGPointMake(x, y);
            [_attributeArray addObject:attris];
        }

    6、立方瀑布流

    屏幕快照 2017-08-09 16.02.21.png

    -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
        //创建一个item布局属性类
        UICollectionViewLayoutAttributes * atti = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        //获取item的个数
        int itemCounts = (int)[self.collectionView numberOfItemsInSection:0];
        //设置每个item的大小为260*100
        atti.size = CGSizeMake(260, 100);
        atti.center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);
        CATransform3D tran3d = CATransform3DIdentity;
        tran3d.m34 = -1/2000.0;
        CGFloat radius = 50/tanf(M_PI*2/itemCounts/2);
    //     CGFloat angle = (float)(indexPath.row)/itemCounts*M_PI*2;
        //获取当前的偏移量
        float offset = self.collectionView.contentOffset.y;
        //在角度设置上,添加一个偏移角度
        float angleOffset = offset/self.collectionView.frame.size.height;
        CGFloat angle = (float)(indexPath.row+angleOffset-1)/itemCounts*M_PI*2;
        tran3d = CATransform3DRotate(tran3d, angle, 1.0, 0, 0);
        tran3d = CATransform3DTranslate(tran3d, 0, 0, radius);
        //进行设置
        atti.transform3D = tran3d;
        return atti;
    }

    7、球形瀑布流

    屏幕快照 2017-08-09 16.05.13.png

     UICollectionViewLayoutAttributes * atti = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        //获取item的个数
        int itemCounts = (int)[self.collectionView numberOfItemsInSection:0];
        atti.center = CGPointMake(self.collectionView.frame.size.width/2+self.collectionView.contentOffset.x, self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);
        atti.size = CGSizeMake(30, 30);
        
        CATransform3D trans3D = CATransform3DIdentity;
        trans3D.m34 = -1/900.0;
        
        CGFloat radius = 15/tanf(M_PI*2/itemCounts/2);
        //根据偏移量 改变角度
        //添加了一个x的偏移量
        float offsety = self.collectionView.contentOffset.y;
        float offsetx = self.collectionView.contentOffset.x;
        //分别计算偏移的角度
        float angleOffsety = offsety/self.collectionView.frame.size.height;
        float angleOffsetx = offsetx/self.collectionView.frame.size.width;
        CGFloat angle1 = (float)(indexPath.row+angleOffsety-1)/itemCounts*M_PI*2;
        //x,y的默认方向相反
        CGFloat angle2 = (float)(indexPath.row+angleOffsetx-1)/itemCounts*M_PI*2;
        //这里我们进行四个方向的排列
        if (indexPath.row%4==1) {
            trans3D = CATransform3DRotate(trans3D, angle1, 1.0,0, 0);
        }else if(indexPath.row%4==2){
            trans3D = CATransform3DRotate(trans3D, angle2, 0, 1, 0);
        }else if(indexPath.row%4==3){
            trans3D = CATransform3DRotate(trans3D, angle1, 0.5,0.5, 0);
        }else{
            trans3D = CATransform3DRotate(trans3D, angle1, 0.5,-0.5,0);
        }
        
        trans3D = CATransform3DTranslate(trans3D, 0, 0, radius);
        
        atti.transform3D = trans3D;

    三、运行效果

    屏幕快照 2017-08-09 15.48.30.png屏幕快照 2017-08-09 15.56.19.png屏幕快照 2017-08-09 16.02.21.png屏幕快照 2017-08-09 16.05.13.png屏幕快照 2017-08-09 16.00.22.png屏幕快照 2017-08-09 15.59.10.png屏幕快照 2017-08-09 15.58.30.png

    注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    IIS 安装 pydio
    Windows环境配置Apache+Mysql+PHP
    Azure 云平台用 SQOOP 将 SQL server 2012 数据表导入 HIVE / HBASE
    PHP 启动 cURL模块以及启动失败的解决方案
    NodeJS 各websocket框架性能分析
    使用AndroidStudio编译NDK的方法及错误解决方案
    Ubuntu 系统下 mongodb 安装和配置
    Ubuntu安装nodeJS
    node.js应用Redis数据库
    Android平台相机接口的应用
  • 原文地址:https://www.cnblogs.com/demodashi/p/8491087.html
Copyright © 2020-2023  润新知