瀑布流实现思路
- 第一种就是用ScrollView来进行实现,由于它不具备复用的功能,因此我们需要自己写一套类似复用的模块来进行优化
- 第二种就是利用apple做好的复用模块,自定义UIColletionLayout来实现瀑布流,想想也是第二种实现起来更快更优,OK,封装一个小小的框架来试试
其他案例
上面的动画切换布局也是自定义UICollectionLayout来进行布局的,简单的静态图片布局展示其实就重写几个方法就可以了
1.prepareLayout
每次重新刷新collectionView的时候会调用一次,做一些初始化的工作
2.layoutAttributesForElementsInRect
返回已经制定好之后的每个cell对应的attribute属性对象进行布局
3.layoutAttributesForItemAtIndexPath
该方法会一直调用,每次cell出来就会根据对应的indexpath来进行方法调用,因此关键布局代码就可以放置在这里进行重新计算
4.collectionViewContentSize
计算整体的大小,实现滚动
上面插入样式实现的传送门
瀑布流实现分析
1.基本变量的声明
static const CGFloat MKJDefaultColumnMargin = 10;
static const CGFloat MKJDefaultRowMargin = 10;
static const UIEdgeInsets MKJDefaultEdgeInsets = {10,10,10,10};
static const NSUInteger MKJDefaultColumnCounts = 2;
@interface MKJWaterFallLayout ()
@property (nonatomic,strong) NSMutableArray *attributeArr;
@property (nonatomic,strong) NSMutableArray *columnHeightArr;
@end
2.初始化
- (void)prepareLayout
{
[super prepareLayout];
[self.columnHeightArr removeAllObjects];
for (NSInteger i = 0; i < [self columnCount]; i++) {
[self.columnHeightArr addObject:@([self insetMargin].top)];
}
[self.attributeArr removeAllObjects];
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < count; i++) {
NSIndexPath *indexpath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:indexpath];
[self.attributeArr addObject:attribute];
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
3.关键计算代码
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attributeArr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGFloat collectionW = self.collectionView.frame.size.width;
CGFloat width = (collectionW - [self insetMargin].left - [self insetMargin].right - ([self columnCount] - 1) * [self columnMargin]) / [self columnCount];
CGFloat height = [self.delegate MKJWaterFallLayout:self heightForItemAtIndexPath:indexPath] * width;
CGFloat minColumnHeight = [self.columnHeightArr[0] doubleValue];
NSUInteger finalCol = 0;
for (NSInteger i = 1 ; i < [self columnCount]; i++) {
CGFloat currentColHeight = [self.columnHeightArr[i] doubleValue];
if (minColumnHeight > currentColHeight) {
minColumnHeight = currentColHeight;
finalCol = i;
}
}
CGFloat x = [self insetMargin].left + (width + [self columnMargin]) * finalCol;
CGFloat y = minColumnHeight;
NSInteger count = indexPath.item;
if ((count / ([self columnCount])) >= 1) {
y += [self rowMargin];
}
att.frame = CGRectMake(x, y, width, height);
self.columnHeightArr[finalCol] = @(CGRectGetMaxY(att.frame));
return att;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
这里的计算简概括为就是对每个cell进行frame的计算
1.宽度的计算是根据列间距和整体左右间距以及行数进行限制,这些参数可以是固定值,也可以是代理传进去的
2.高度的计算必定是根据外部代理进行计算的
3.X值的计算是根据这个框架内部的每一列的高度数组进行之前的缓存高度,进行最小值计算,然后拿出最小值对应的列数,根据上面算出来的高度进行X值的计算
4.Y值的计算和X值一样,根据给定的数组,比出最小高度列的列数,根据数组的高度,计算出对应的Y值,最终再进行数组对应列数的高度更新
4.获取实际能容大小,让其可以滚动
- (CGSize)collectionViewContentSize
{
CGFloat maxColumHeight = [self.columnHeightArr[0] doubleValue];
for (NSInteger i = 1; i < [self columnCount]; i++) {
CGFloat currentColHeight = [self.columnHeightArr[i] doubleValue];
if (maxColumHeight < currentColHeight) {
maxColumHeight = currentColHeight;
}
}
return CGSizeMake(0, maxColumHeight + [self insetMargin].bottom);
}
5.这个小框架已经封装好了,简单介绍下用法
- (UICollectionView *)colletionView
{
if (_colletionView == nil) {
MKJWaterFallLayout *layout = [[MKJWaterFallLayout alloc] init];
layout.delegate = self;
UICollectionView *collectionV = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
collectionV.backgroundColor = [UIColor redColor];
[collectionV registerNib:[UINib nibWithNibName:productID bundle:nil] forCellWithReuseIdentifier:productID];
collectionV.delegate = self;
collectionV.dataSource = self;
_colletionView = collectionV;
}
return _colletionView;
}
#pragma mark - waterfallLayoutDelegate
- (CGFloat)MKJWaterFallLayout:(MKJWaterFallLayout *)layout heightForItemAtIndexPath:(NSIndexPath *)indexPath
{
MKJProductModel *product = self.dataSource[indexPath.item];
return product.h / product.w;
}
- (CGFloat)columnMarginForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return 10;
}
- (CGFloat)rowMarginForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return 30;
}
- (NSUInteger)columnCountForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return 3;
}
- (UIEdgeInsets)insetForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return UIEdgeInsetsMake(10, 10, 10, 10);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
一个简单的Demo以及一个框架就这样基本设计完了,需要的朋友可以直接下载拖到自己的工程里面试试,有问题请留言
Demo地址:
Demo和框架传送门