美文网首页
day3:网格瀑布流布局&&解决cell复用导致的布局紊乱问题

day3:网格瀑布流布局&&解决cell复用导致的布局紊乱问题

作者: IOS_技术小牛 | 来源:发表于2019-10-31 14:08 被阅读0次

    瀑布流布局

    首先 常见的瀑布流布局有三种方式,表格,网格,滚动视图。
    但是大部分都会使用网格。

    最基本的网格创建不多说了;
    一共遇到了一下几个坑:
    01:瀑布流布局后的头视图问题;
    02:瀑布流布局下的复用问题;

    在研究解决问题之前先来实现一下这个瀑布流

    我们先创建 一个网格视图;
    用过网格的都知道它是用UICollectionViewLayout来布局的
    但是UICollectionViewFlowLayout应该是更契合的。
    所以我还是选择用UICollectionViewLayout来实现瀑布流布局
    这两个有什么区别,我也不清楚,就算会有问题,
    那就等遇到的时候再说吧!!!

    新建一个继承UICollectionViewLayout的视图类

    .h
    、、、
    //
    // WaterFlowLayout.h
    // WaterFlow
    //
    // Created by 张东辉 on 2019/10/30.
    // Copyright © 2019 ZDH. All rights reserved.
    //
    /*
    */

    import <UIKit/UIKit.h>

    @class WaterFlowLayout;
    @protocol WaterFlowLayoutDelegate<NSObject>//提供一个代理方法
    @required//必须实现方法
    //必须实现方法.
    -(CGFloat)waterFlowLayout:(WaterFlowLayout *)layout heightForItemAtIndex:(NSInteger)index itemWidth:(CGFloat)itemWidth;
    @optional//非必须实现方法
    -(NSInteger)waterFlowLayoutColumnCount:(WaterFlowLayout *)layout;//返回列数
    -(CGFloat)waterFlowLayoutColumnSpacing:(WaterFlowLayout *)layout;//列间距
    -(CGFloat)waterFlowLayoutRowSpacing:(WaterFlowLayout *)layout;//行间距
    -(UIEdgeInsets)waterFlowLayoutEdgeInsets:(WaterFlowLayout *)layout;//四边距

    @end
    @interface WaterFlowLayout : UICollectionViewLayout
    @property(nonatomic,strong)NSMutableArray *attrArray;//布局数组
    @property(nonatomic,strong)NSMutableArray *maxYArray;//计算每一列高度数组
    @property(nonatomic,weak) id<WaterFlowLayoutDelegate>delegate;
    @end

    、、、

    .m
    、、、

    //
    // WaterFlowLayout.m
    // WaterFlow
    //
    // Created by 张东辉 on 2019/10/30.
    // Copyright © 2019 ZDH. All rights reserved.
    //

    import "WaterFlowLayout.h"

    pragma mark - 声明常量

    static NSInteger const DefaultColumnCount = 2; //默认列数
    static CGFloat const DefaultColumnSpacing = 10; //默认列间距
    static CGFloat const DefaultRowSpacing= 10; //默认行边距
    static UIEdgeInsets const DefaultEdgeInsets = {10,10,10,10};//距离父视图的边距
    @interface WaterFlowLayout ()

    //这里判断是否通过方法外部传进参数,如果没穿使用本地默认参数;
    -(NSInteger)columnCount; //返回列数
    -(CGFloat)columnSpacing; //返回列间距
    -(CGFloat)rowSpacing; //返回行间距
    -(UIEdgeInsets)edgeInsets; //返回距离父视图的边距

    @end

    @implementation WaterFlowLayout

    pragma mark - lazy init

    -(NSInteger)columnCount{
    if ([self.delegate respondsToSelector:@selector(waterFlowLayoutColumnCount:)]) {
    return [self.delegate waterFlowLayoutColumnCount:self];
    }
    return DefaultColumnCount;
    }
    -(CGFloat)columnSpacing{
    if ([self.delegate respondsToSelector:@selector(waterFlowLayoutColumnSpacing:)]) {
    return [self.delegate waterFlowLayoutColumnSpacing:self];
    }
    return DefaultColumnSpacing;

    }
    -(CGFloat)rowSpacing{
    if ([self.delegate respondsToSelector:@selector(waterFlowLayoutRowSpacing:)]) {
    return [self.delegate waterFlowLayoutRowSpacing:self];
    }
    return DefaultRowSpacing;
    }
    -(UIEdgeInsets)edgeInsets{
    if ([self.delegate respondsToSelector:@selector(waterFlowLayoutEdgeInsets:)]) {
    return [self.delegate waterFlowLayoutEdgeInsets:self];
    }
    return DefaultEdgeInsets;
    }

    -(NSMutableArray *)maxYArray {
    if(!_maxYArray) {
    _maxYArray = [NSMutableArray array];

    }
    return _maxYArray ;
    

    }
    -(NSMutableArray *)attrArray{
    if (!_attrArray) {
    _attrArray = [NSMutableArray array];
    }
    return _attrArray;

    }

    pragma mark - 实现代码

    //在我们布局之前,再次方法做些初始化设置 此方法只有在初始化或者刷新的时候调用.
    -(void)prepareLayout{
    [super prepareLayout];

    //每一次调用方法,都用清空一下数组.在产生新的布局之前清空一下旧的布局.防止数据混乱.
    [self.attrArray removeAllObjects];
    [self.maxYArray removeAllObjects];
    
    for (NSInteger i = 0; i<[self columnCount]; i++) {
        [self.maxYArray addObject:@([self edgeInsets].top)];
    }
    
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    
    for (NSInteger i=0;  i<itemCount; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        
     //手动调用attributes方法
        [self.attrArray addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
    }
    

    }

    //用来记录当前每个item的layout属性 布局专用
    -(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{

    return self.attrArray;
    

    }

    //每一个Item的Frame特点;核心计算方法
    -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{

    //初始化
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    NSInteger __block minHeightColumn = 0;
    NSInteger __block minHeight = [self.maxYArray[minHeightColumn] floatValue];
    //使用block遍历所有item高度数组,取出最短的长度.让下一个item放在当前最短列;
    [self.maxYArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CGFloat collumnHeight = [(NSNumber *)obj floatValue];
        if (minHeight > collumnHeight) {
            minHeight = collumnHeight;
            minHeightColumn = idx;//更新列号
        }
    }];
    
    
    UIEdgeInsets edgeInsets = [self edgeInsets];
    
    //宽度
    CGFloat width = (CGRectGetWidth(self.collectionView.frame)-edgeInsets.left - edgeInsets.right - [self columnSpacing] * ([self columnCount]-1))/[self columnCount];
    
    
    //高度
    CGFloat height =  [self.delegate waterFlowLayout:self heightForItemAtIndex:indexPath.item itemWidth:width];
    
    //起始X轴坐标
    CGFloat originX = edgeInsets.left + minHeightColumn *(width + [self columnSpacing]);
    
    //起始Y轴坐标
    CGFloat originY = minHeight;
    //判断是不是第一行,如果第一行就不加上边距了
    if (originY != edgeInsets.top) {
        originY += [self rowSpacing];
    }
    
    
    [attributes setFrame:CGRectMake(originX, originY, width, height)];
    self.maxYArray[minHeightColumn] = @(CGRectGetMaxY(attributes.frame));
    
    
    return attributes;
    

    }

    //重写父类方法,不然无法滚动.🤢
    -(CGSize)collectionViewContentSize{

    NSInteger __block maxHeight = 0;
    //获取所有列中最长的那一列
    
    [self.maxYArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CGFloat collumnHeight = [(NSNumber *)obj floatValue];
        if (maxHeight < collumnHeight) {
            maxHeight = collumnHeight;
           
        }
    }];
    
    return CGSizeMake(0, maxHeight+[self edgeInsets].bottom);
    

    }
    @end

    、、、

    完事,在vc里面直接用这个布局类就完事了

    然后第一个问题来了

    瀑布流布局后的头视图问题:

    因为网格的头视图也是需要注册的,我们是继承自UICollectionViewLayout的里面没有注册
    头视图的方法;(报应来了,但是我头铁);
    所以怎么搞,我是直接把传进去的上边距拉倒自己想要的大小;
    直接创建一个view加到网格视图上。没区别;
    当然不是最好的方法;
    有更好的方法请指正;

    草率的解决了!!!

    下一个问题

    瀑布流布局下的复用问题:

    因为是瀑布流布局,所以每一个cell高度都不一样,导致cell复用的时候会出现复用下来的图片大小混乱。恶心死我了真的是。

    网上见到一些取消复用的,试了试不知道为啥不好使。
    直到有一天..............(.....)
    嗯对,找到正确的姿势了

    我们先给一个数组来存放所有的复用标识符;
    每一个indexpath的都是不一样的标识符;
    判断一个没有就创建。

    、、、
    //解决瀑布流因为cell重用导致的
    // 每次先从字典中根据IndexPath取出唯一标识符
    NSString *identifier = [_cellDic objectForKey:[NSString stringWithFormat:@"%@",indexPath]];
    // 如果取出的唯一标示符不存在,则初始化唯一标示符,并将其存入字典中,对应唯一标示符注册Cell
    if (identifier == nil) {
    identifier = [NSString stringWithFormat:@"identifier%@",[NSString stringWithFormat:@"%@",indexPath]];
    [_cellDic setValue:identifier forKey:[NSString stringWithFormat:@"%@",indexPath]];
    //注册cell
    [self.collecview registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:identifier];
    }

    CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    

    、、、

    就这样,但是总觉得今天的代码很有问题。我还不知道会有啥问题;
    到这吧!

    相关文章

      网友评论

          本文标题:day3:网格瀑布流布局&&解决cell复用导致的布局紊乱问题

          本文链接:https://www.haomeiwen.com/subject/phftbctx.html