本文是建立在OC对象来进行对瀑布流视图布局的,一下会按照UICollectionView的初始化开始为您展示,流程如下:
3. UICollectionView需要的collectionCell自定义控件
4. 瀑布流需要展示对象model
1. UICollectionView的初始化----->>>> 2. UICollectionViewFlowLayout
----- >>>>> 3. UICollectionView需要的collectionCell自定义控件,4. -------->>>> 瀑布流需要展示对象model
1. UICollectionView的初始化
#import "ViewController.h"
#import "WaterfallFlowLayout.h"
#import "WaterfallModel.h"
#import "WaterfallCollectionViewCell.h"
#define IdentifierWaterfall @"identifierWaterfall"
@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate>
// 商品列表数组
@property (nonatomic, strong) NSMutableArray *goodsList;
// 当前的数据索引
@property (nonatomic, assign) NSInteger index;
@property (nonatomic, strong) WaterfallFlowLayout *waterfallFlowLayout;
@property (nonatomic, strong) UICollectionView *waterCollection;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_goodsList = [NSMutableArray array];
_waterfallFlowLayout = [[WaterfallFlowLayout alloc]init];
_waterfallFlowLayout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
_waterfallFlowLayout.minimumInteritemSpacing = 10;
_waterfallFlowLayout.minimumLineSpacing = 0;
_waterCollection = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT-64) collectionViewLayout:_waterfallFlowLayout];
_waterCollection.scrollEnabled = YES;
_waterCollection.dataSource = self;
_waterCollection.delegate = self;
_waterCollection.backgroundColor = [UIColor groupTableViewBackgroundColor];
[_waterCollection registerClass:[WaterfallCollectionViewCell class] forCellWithReuseIdentifier:IdentifierWaterfall];
self.automaticallyAdjustsScrollViewInsets = NO;
[self.view addSubview:_waterCollection];
[self loadData];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/**
* 加载数据
*/
- (void)loadData {
NSArray *goods = [WaterfallModel waterModelWithIndex:self.index];
[self.goodsList addObjectsFromArray:goods];
[self.goodsList removeObjectsInRange:NSMakeRange(25, self.goodsList.count-25)];
self.index++;
// 设置布局的属性
_waterfallFlowLayout.columnCount = 3;
_waterfallFlowLayout.goodsList = self.goodsList;
// 刷新数据
[self.waterCollection reloadData];
}
#pragma mark - 数据源方法
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSLog(@"---%li",self.goodsList.count);
return self.goodsList.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// 创建可重用的cell
WaterfallCollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:IdentifierWaterfall
forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0];
cell.titleLabel.text = [NSString stringWithFormat:@"%li",indexPath.row];
// cell.waterfallModel = self.goodsList[indexPath.item];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"\n section: %li row: %li ",indexPath.section,indexPath.row);
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
#pragma mark - 懒加载
- (NSMutableArray *)goodsList {
if (_goodsList == nil) {
_goodsList = [NSMutableArray array];
}
return _goodsList;
}
@end
#import <UIKit/UIKit.h>
@interface WaterfallFlowLayout : UICollectionViewFlowLayout
// 总列数
@property (nonatomic, assign) NSInteger columnCount;
// 商品数据数组
@property (nonatomic, strong) NSArray *goodsList;
@end
#import "WaterfallFlowLayout.h"
#import "WaterfallModel.h"
@interface WaterfallFlowLayout ()
// 所有item的属性的数组
@property (nonatomic, strong) NSArray *layoutAttributesArray;
@end
@implementation WaterfallFlowLayout
/**
* 布局准备方法 当collectionView的布局发生变化时 会被调用
* 通常是做布局的准备工作 itemSize.....
* UICollectionView 的 contentSize 是根据 itemSize 动态计算出来的
*/
- (void)prepareLayout {
// 根据列数 计算item的宽度 宽度是一样的
CGFloat contentWidth = self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right;
CGFloat marginX = self.minimumInteritemSpacing;
CGFloat itemWidth = (contentWidth - marginX * (self.columnCount - 1)) / self.columnCount;
// 计算布局属性
[self computeAttributesWithItemWidth:itemWidth];
}
/**
* 根据itemWidth计算布局属性
*/
- (void)computeAttributesWithItemWidth:(CGFloat)itemWidth {
// 定义一个列高数组 记录每一列的总高度
CGFloat columnHeight[self.columnCount];
// 定义一个记录每一列的总item个数的数组
NSInteger columnItemCount[self.columnCount];
// 初始化
for (int i = 0; i < self.columnCount; i++) {
columnHeight[i] = self.sectionInset.top;
columnItemCount[i] = 0;
}
// 遍历 goodsList 数组计算相关的属性
NSInteger index = 0;
NSMutableArray *attributesArray = [NSMutableArray arrayWithCapacity:self.goodsList.count];
for (WaterfallModel *good in self.goodsList) {
// 建立布局属性
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
// 找出最短列号
NSInteger column = [self shortestColumn:columnHeight];
// 数据追加在最短列
columnItemCount[column]++;
// X值
CGFloat itemX = self.sectionInset.left + (itemWidth + self.minimumInteritemSpacing) * column ;
// Y值
CGFloat itemY = columnHeight[column];
// 等比例缩放 计算item的高度
CGFloat itemH = (good.h *1.00* itemWidth) / good.w;
// 设置frame
attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemH);
[attributesArray addObject:attributes];
// 累加列高
columnHeight[column] += itemH + self.minimumLineSpacing;
index++;
}
// 找出最高列列号
NSInteger column = [self highestColumn:columnHeight];
NSLog(@"-----%li %f",column,columnHeight[column]);
// 根据最高列设置itemSize 使用总高度的平均值
/**
* 1、此处的column包含一个section,总高度包含(self.sectionInset.top + self.sectionInset.bottom + (cell_height + self.minimumLineSpacing) *columnItemCount[column] )
*/
CGFloat itemH = (columnHeight[column] - self.sectionInset.top - self.minimumLineSpacing * columnItemCount[column]) * 1.00 / columnItemCount[column];
self.itemSize = CGSizeMake(itemWidth, itemH);
// // 添加页脚属性
// NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:0 inSection:0];
// UICollectionViewLayoutAttributes *footerAttr = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:footerIndexPath];
// footerAttr.frame = CGRectMake(0, columnHeight[column], self.collectionView.bounds.size.width, 50);
// [attributesArray addObject:footerAttr];
// 给属性数组设置数值
self.layoutAttributesArray = attributesArray.copy;
}
/**
* 找出columnHeight数组中最短列号 追加数据的时候追加在最短列中
*/
- (NSInteger)shortestColumn:(CGFloat *)columnHeight {
CGFloat max = CGFLOAT_MAX;
NSInteger column = 0;
for (int i = 0; i < self.columnCount; i++) {
if (columnHeight[i] < max) {
max = columnHeight[i];
column = i;
}
}
return column;
}
/**
* 找出columnHeight数组中最高列号
*/
- (NSInteger)highestColumn:(CGFloat *)columnHeight {
CGFloat min = 0;
NSInteger column = 0;
for (int i = 0; i < self.columnCount; i++) {
if (columnHeight[i] > min) {
min = columnHeight[i];
column = i;
}
}
return column;
}
/**
* 跟踪效果:当到达要显示的区域时 会计算所有显示item的属性
* 一旦计算完成 所有的属性会被缓存 不会再次计算
* @return 返回布局属性(UICollectionViewLayoutAttributes)数组
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
// 直接返回计算好的布局属性数组
return self.layoutAttributesArray;
}
@end
3. UICollectionView需要的collectionCell自定义控件
#import <UIKit/UIKit.h>
@class WaterfallModel;
@interface WaterfallCollectionViewCell : UICollectionViewCell
@property (nonatomic, strong) WaterfallModel *waterfallModel;
@property (nonatomic, strong) UILabel *titleLabel;
@end
#import "WaterfallCollectionViewCell.h"
#import "WaterfallModel.h"
@interface WaterfallCollectionViewCell()
@end
@implementation WaterfallCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self.contentView addSubview:self.titleLabel];
self.titleLabel.frame = CGRectMake(0, 0, self.bounds
.size.width, self.bounds.size.height);
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
}
return self;
}
- (instancetype)init {
self = [super init];
if (self) {
}
return self;
}
- (void)setWaterfallModel:(WaterfallModel *)waterfallModel {
_waterfallModel = waterfallModel;
self.titleLabel.text = waterfallModel.price;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.textAlignment = NSTextAlignmentCenter;
// _titleLabel.backgroundColor = [UIColor greenColor];
}
return _titleLabel;
}
@end
4. 瀑布流需要展示对象model
#import <Foundation/Foundation.h>
@interface WaterfallModel : NSObject
@property (nonatomic, assign) NSInteger h; // 商品图片高
@property (nonatomic, assign) NSInteger w; // 商品图片宽
@property (nonatomic, copy) NSString *img; // 商品图片地址
@property (nonatomic, copy) NSString *price; // 商品价格
+ (instancetype)waterModelWithDict:(NSDictionary *)dict; // 字典转模型
+ (NSArray *)waterModelWithIndex:(NSInteger)index; // 根据索引返回商品数组
@end
#import "WaterfallModel.h"
@implementation WaterfallModel
/**
* 字典转模型
*/
+ (instancetype)waterModelWithDict:(NSDictionary *)dict {
id waterModel = [[self alloc] init];
[waterModel setValuesForKeysWithDictionary:dict];
return waterModel;
}
/**
* 根据索引返回商品数组
*/
+ (NSArray *)waterModelWithIndex:(NSInteger)index {
NSString *fileName = [NSString stringWithFormat:@"%ld.plist", index % 3 + 1];
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
NSArray *goodsArray = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:goodsArray.count];
[goodsArray enumerateObjectsUsingBlock: ^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
[tmpArray addObject:[self waterModelWithDict:dict]];
}];
return tmpArray.copy;
}
@end
如需原文,可点击这里查看