• IOS 瀑布流UICollectionView实现


    IOS 瀑布流UICollectionView实现


    在实现瀑布流之前先来看看瀑布流的雏形(此方法的雏形 UICollectionView)

    对于UICollectionView我们有几点注意事项

    • 它和tableView不一样,ContentView的内容完全需要我们自己去添加。
    • 它与tableview相比,他的初始化需要FlowLayout并且大部分操作在其上。
    • UIcollectionView的实用性极强,虽然有时他并不是最好的解决方案,但是它可以很灵活的实现各种效果。

    图(一)

    如图,模拟器上展示的是很多方格,但是值得注意的是他们是有规则的。

    虽然看上去很整洁但是并不美观。

    我们所说的要实现瀑布流就是要实现它的不整洁,但是规律(这里我说的是规律)

    正题

    前面说了UIcollectionView的大部分操作在FlowLayout上,当然也包括格局部署。

    为了实现瀑布流我们所要实现的便是改变他的格局部署。

    在写代码前先确定一下实现思想。

    • 需要什么???
      • 首先我们需要确定瀑布流的显示风格
      • 然后根据确定好的风格进行整体设计
      • 最后通过细节的处理完善代码
        • 我们需要什么样的风格???
          • 我们需要的是实现将上面图片中的布局改变为不等高的效果
          • 说的俗一点就是像名字一样,像瀑布流水一样
        • 整体该如何设计???
          • 整体采用与上面图片一样的设计方法,每个模块都是一个cell
          • 确保最上面一行的cell的y值相同(美观)
          • 确保不不会出现一列特别长,一列特别短的效果
        • 初步细节有哪些???
          • 因为每个cell的height不同,所以我们要考虑放置的顺序应该是什么
          • 精简代码(这是每个项目必须注意的)

    实现效果

    代码

    下面是实现的代码部分(不提供demo了 很简单)

    我在注释中简单介绍。

    ---

    复制代码
    //
    //  ViewController.m
    //  CX-瀑布流UIcollectionView实现
    //
    //  Created by ma c on 16/4/8.
    //  Copyright © 2016年 bjsxt. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "CXCollectionViewCell.h"
    #import "CXCollectionViewLayout.h"
    
    static NSString * identifier = @"cellID";
    
    @interface ViewController ()<UICollectionViewDataSource>
    //所要展示的UICollectionView
    @property (nonatomic, strong) UICollectionView * collectionView;
    
    @end
    
    @implementation ViewController
    
    #pragma mark - <懒加载>
    - (UICollectionView *)collectionView {
        if (!_collectionView) {
            //初始化我们自定义的flowLayout
            CXCollectionViewLayout * flowLayout = [[CXCollectionViewLayout alloc]init];
            //初始化collectionView
            _collectionView = [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
            //设置数据源(collectionView的命根子)
            _collectionView.dataSource = self;
            //注册我们自定义的cell
            [_collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CXCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:identifier];
        }
        return _collectionView;
    }
    
    
    #pragma mark - <life>
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //在self.view上添加---
        [self.view addSubview:self.collectionView];
    }
    #pragma mark - <UICollectionViewDataSource>
    //这里返回的是item的个数 返回100
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
        
        return 100;
    }
    //这里返回的是cell 我们可以在这里进行一些简单的操作
    -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
        
        CXCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
        //为了瀑布流的实现细节我们添加的Label
        
        cell.label.text = [NSString stringWithFormat:@"%zd",indexPath.item];
        //cell的背景色
        cell.backgroundColor = [UIColor orangeColor];
        
        return cell;
    }
    
    @end
    复制代码

    ---

    复制代码
    //
    //  CXCollectionViewLayout.m
    //  CX-瀑布流UIcollectionView实现
    //
    //  Created by ma c on 16/4/8.
    //  Copyright © 2016年 bjsxt. All rights reserved.
    //
    
    #import "CXCollectionViewLayout.h"
    
    //瀑布流的列数
    static NSInteger CXcolumnCount = 3;
    //瀑布流的内边距
    static UIEdgeInsets CXdefaultEdgeInsets = {20,15,10,15};
    //cell的列间距
    static NSInteger CXcolumnMagin = 10;
    //cell的行间距
    static NSInteger CXrowMagin = 10;
    
    @interface CXCollectionViewLayout ()
    
    //存放所有cell 的布局属性
    @property (nonatomic, strong) NSMutableArray * CXattrsArray;
    //缩放所有列的高度
    @property (nonatomic, strong) NSMutableArray * CXcolumnHeights;
    
    @end
    
    @implementation CXCollectionViewLayout
    
    #pragma mark - <懒加载>
    - (NSMutableArray *)CXattrsArray{
        if (!_CXattrsArray) {
            _CXattrsArray = [NSMutableArray array];
        }
        return _CXattrsArray;
    }
    
    - (NSMutableArray *)CXcolumnHeights{
        if (!_CXcolumnHeights) {
            _CXcolumnHeights = [NSMutableArray array];
        }
        return _CXcolumnHeights;
    }
    #pragma mark - <准备布局>
    //准备布局(布局前自动执行)
    - (void) prepareLayout{
        //重写此方法一定要记得super
        [super prepareLayout];
        
        //在实际操作中我们的数据并不会固定不变的,因此我们每次布局前最好要清空之前存储的属性
        //清空存放所有列的高度
        //清空存放所有cell的不去属性
        [self.CXcolumnHeights removeAllObjects];
        [self.CXattrsArray removeAllObjects];
        //首先为第一行的cell附高度
        for (NSInteger i = 0; i < CXcolumnCount; i ++) {
            //数组里只能存放对象
            [self.CXcolumnHeights addObject:@(CXdefaultEdgeInsets.top)];
        }
        //下面开始创建每一个cell的布局属性 并且添加到存储cell布局属性的数组中
        //cell总个数 因为这里只要一个section
        NSInteger count = [self.collectionView numberOfItemsInSection:0];
        for (NSInteger i = 0; i < count; i ++) {
            // 创建位置 即indexPath
            NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
            //获取indexPath对应的cell布局属性
            UICollectionViewLayoutAttributes * attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
            //把获取到的布局属性添加到数组中
            [self.CXattrsArray addObject:attributes];
        }
        //准备布局的工作到这里就结束了
    }
    //返回所有cell布局属性 及整体cell 的排布
    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
        return self.CXattrsArray;
    }
    //返回cell 的布局属性
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
        //创建布局属性
        UICollectionViewLayoutAttributes * CXattributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        //获取collectionView 的宽
        CGFloat collectionViewWidth = self.collectionView.frame.size.width;
        //下面的一部分是获取cell的frame(布局属性)
        CGFloat width;
        CGFloat height;
        CGFloat X;
        CGFloat Y;
        //获取width
        width = (collectionViewWidth - CXdefaultEdgeInsets.left - CXdefaultEdgeInsets.right - (CXcolumnCount - 1) * CXcolumnMagin) / CXcolumnCount;
        //获取height
        //在实际开发中heigh并不是真正的随机 而是根据数据来决定height 在这里展示初步的介绍其原理 因此采用大于100小于150的随机数
        height = 100 + arc4random_uniform(50);
        //获取X (瀑布流的实现重点就在cell的X,Y值获取)
        //设置一个列数的中间变量
        NSInteger tempColumn = 0;
        //设置高度小的中间变量 在这里我们把第0列的高度给他,这样可以减少循环次数,提高效率
        CGFloat minColumnHeight = [self.CXcolumnHeights[0] doubleValue];
        for (NSInteger i = 1; i < CXcolumnCount; i ++) {
            if (minColumnHeight > [self.CXcolumnHeights[i] doubleValue]) {
                minColumnHeight = [self.CXcolumnHeights[i] doubleValue];
                tempColumn = i;
            }
        }
        X = CXdefaultEdgeInsets.left + (width + CXcolumnMagin) * tempColumn;
        //获取Y
        Y = minColumnHeight;
        if (Y != CXdefaultEdgeInsets.top) {
            Y += CXrowMagin;
        }
        //设置cell的frame
        CXattributes.frame = CGRectMake(X, Y, width, height);
        //更新高度最矮的那列的高度
        self.CXcolumnHeights[tempColumn] = @(CGRectGetMaxY(CXattributes.frame));
        
        return CXattributes;
    }
    //返回collegeView的Content的大小
    - (CGSize)collectionViewContentSize{
        //虽说返回的是大小,但是我们这里主要的是height
        CGFloat maxColumnHeight = [self.CXcolumnHeights[0] doubleValue];
        for (NSInteger i = 1; i < CXcolumnCount; i++) {
    
            CGFloat columnHeight = [self.CXcolumnHeights[i] doubleValue];
            
            if (maxColumnHeight < columnHeight) {
                maxColumnHeight = columnHeight;
            }
        }
        return CGSizeMake(0, maxColumnHeight + CXdefaultEdgeInsets.bottom);
        
    }
    
    @end
    复制代码

    到此为止瀑布流的实现也就结束了。

    在这里说明几点值得注意的地方。

    • 瀑布流中的cell排布顺势是根据当前列的高度有关的(例如:如果当前第三列是最短的,但是按正常情况下cell应该排在第一列,那么这个时候,新的cell会排在第三列,这是为了避免某一列高度特别长或某一列的高度特别短)
    • 在实际应用中通常cell的大小是根据数据的来处理的
    • UIcollectionView的content的高度是不确定的,因此我们要根据内容设定高度。
    • 当涉及到刷新的时候我们要注意cell的布局属性是否在新数据到来前清空了。
  • 相关阅读:
    慎用ViewController的跳转
    Cocos2d中从场景切换到UIViewController视图方法总结
    presentModalViewController
    NSString / NSMutableString 字符串处理,常用代码 (实例)
    NSXMLParser解析xml
    preformselector 多个参数设置
    UITableView 异步加载图片缓存 笔记
    消息通信机制NSNotificationCenter
    UITableView进行讲解 真的是太详细了
    CLR读书笔记第二章 生成,打包,部署和管理应用程序及类型
  • 原文地址:https://www.cnblogs.com/wuyuxin/p/7045653.html
Copyright © 2020-2023  润新知