瀑布流,又称瀑布流式布局。是比较流行的一种页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。
说明:(1)瀑布流每一个的宽度是一样的,都是高度不一样(2)补齐算法,哪里比较短就补哪里,不是简单的从左到右排(两列之间的差距越来越大)。
在ios中目前比较好的实现方式有2种。1.仿照UITableView思路自定义ScrollView 2.使用UICollectionView,自定义UICollectionViewLayout实现.
使用UICollectionView,自定义UICollectionViewLayout实现
ViewController.m
#import "ViewController.h"
#import "WaterflowLayout.h"
@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, HMWaterflowLayoutDelegate>
@property (nonatomic, weak) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *shops;
@end
@implementation ViewController
- (NSMutableArray *)shops
{
if (_shops == nil) {
self.shops = [NSMutableArray array];
}
return _shops;
}
static NSString *const ID = @"shop";
- (void)viewDidLoad {
[super viewDidLoad];
// 1.初始化数据
NSArray *shopArray = [Shop objectArrayWithFilename:@"1.plist"];
[self.shops addObjectsFromArray:shopArray];
WaterflowLayout *layout = [[WaterflowLayout alloc] init];
layout.delegate = self;
// layout.sectionInset = UIEdgeInsetsMake(100, 20, 40, 30);
// layout.columnMargin = 20;
// layout.rowMargin = 30;
// layout.columnsCount = 4;
// 2.创建UICollectionView
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
collectionView.backgroundColor = [UIColor whiteColor];
collectionView.dataSource = self;
collectionView.delegate = self;
[collectionView registerNib:[UINib nibWithNibName:@"ShopCell" bundle:nil] forCellWithReuseIdentifier:ID];
[self.view addSubview:collectionView];
self.collectionView = collectionView;
// 3.增加刷新控件
[self.collectionView addFooterWithTarget:self action:@selector(loadMoreShops)];
}
- (void)loadMoreShops
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSArray *shopArray = [Shop objectArrayWithFilename:@"1.plist"];
[self.shops addObjectsFromArray:shopArray];
[self.collectionView reloadData];
[self.collectionView footerEndRefreshing];
});
}
#pragma mark - <WaterflowLayoutDelegate>
- (CGFloat)waterflowLayout:(WaterflowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath
{
Shop *shop = self.shops[indexPath.item];
return shop.h / shop.w * width;
}
#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.shops.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
cell.shop = self.shops[indexPath.item];
return cell;
}
@end
WaterflowLayout.h
#import <UIKit/UIKit.h>
@class WaterflowLayout;
@protocol WaterflowLayoutDelegate <NSObject>
- (CGFloat)waterflowLayout:(WaterflowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath;
@end
@interface WaterflowLayout : UICollectionViewLayout
@property (nonatomic, assign) UIEdgeInsets sectionInset;
/** 每一列之间的间距 */
@property (nonatomic, assign) CGFloat columnMargin;
/** 每一行之间的间距 */
@property (nonatomic, assign) CGFloat rowMargin;
/** 显示多少列 */
@property (nonatomic, assign) int columnsCount;
@property (nonatomic, weak) id<WaterflowLayoutDelegate> delegate;
@end
WaterflowLayout.m
#import "WaterflowLayout.h"
//static const CGFloat ColumnMargin = 10;
//static const CGFloat RowMargin = ColumnMargin;
@interface WaterflowLayout();
/** 这个字典用来存储每一列最大的Y值(每一列的高度) */
@property (nonatomic, strong) NSMutableDictionary *maxYDict;
/** 存放所有的布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end
@implementation WaterflowLayout
- (NSMutableDictionary *)maxYDict
{
if (!_maxYDict) {
self.maxYDict = [[NSMutableDictionary alloc] init];
}
return _maxYDict;
}
- (NSMutableArray *)attrsArray
{
if (!_attrsArray) {
self.attrsArray = [[NSMutableArray alloc] init];
}
return _attrsArray;
}
- (instancetype)init
{
if (self = [super init]) {
self.columnMargin = 10;
self.rowMargin = 10;
self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
self.columnsCount = 3;
}
return self;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
/**
* 每次布局之前的准备
*/
- (void)prepareLayout
{
[super prepareLayout];
// 1.清空最大的Y值
for (int i = 0; i<self.columnsCount; i++) {
NSString *column = [NSString stringWithFormat:@"%d", i];
self.maxYDict[column] = @(self.sectionInset.top);
}
// 2.计算所有cell的属性
[self.attrsArray removeAllObjects];
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (int i = 0; i<count; i++) {
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[self.attrsArray addObject:attrs];
}
}
/**
* 返回所有的尺寸
*/
- (CGSize)collectionViewContentSize
{
__block NSString *maxColumn = @"0";
[self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
if ([maxY floatValue] > [self.maxYDict[maxColumn] floatValue]) {
maxColumn = column;
}
}];
return CGSizeMake(0, [self.maxYDict[maxColumn] floatValue] + self.sectionInset.bottom);
}
/**
* 返回indexPath这个位置Item的布局属性
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
// 假设最短的那一列的第0列
__block NSString *minColumn = @"0";
// 找出最短的那一列
[self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
if ([maxY floatValue] < [self.maxYDict[minColumn] floatValue]) {
minColumn = column;
}
}];
// 计算尺寸
CGFloat width = (self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right - (self.columnsCount - 1) * self.columnMargin)/self.columnsCount;
CGFloat height = [self.delegate waterflowLayout:self heightForWidth:width atIndexPath:indexPath];
// 计算位置
CGFloat x = self.sectionInset.left + (width + self.columnMargin) * [minColumn intValue];
CGFloat y = [self.maxYDict[minColumn] floatValue] + self.rowMargin;
// 更新这一列的最大Y值
self.maxYDict[minColumn] = @(y + height);
// 创建属性
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attrs.frame = CGRectMake(x, y, width, height);
return attrs;
}
/**
* 返回rect范围内的布局属性
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
@end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)