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

瀑布流布局

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

最基本的网格创建不多说了;
一共遇到了一下几个坑:
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];

、、、

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容