前段時(shí)間在逛淘寶的時(shí)候發(fā)現(xiàn)淘寶的商品界面的布局是瀑布流(我記得明明之前不是瀑布流的??)。剛好手上活忙完了,寫(xiě)了一個(gè)瀑布流的布局,簡(jiǎn)單的封裝了下,以便日后使用??。其實(shí)說(shuō)到底瀑布流也就是UICollectionView做的,只是修改了CollectionView的流式布局(FlowLayout),以后要用就直接把自定義的FlowLayout拿過(guò)來(lái)用就行了。
瀑布流
1.要有瀑布流首先得有colletionView,所以先在viewController中把我們的colletionView弄出來(lái)。因?yàn)闆](méi)有做網(wǎng)絡(luò)請(qǐng)求,所以現(xiàn)在我模擬了一份數(shù)據(jù),都是在plist里面裝著在呢。
plist數(shù)據(jù)
相關(guān)圖片
這里強(qiáng)調(diào)一下,因?yàn)槲沂侵苯佑胹b的方式加載的,所以代理啊,數(shù)據(jù)源啊都不用寫(xiě)了,包括后面自定義cell中的那些控件都是直接拉線連的。那這里我們就直接上數(shù)據(jù)源方法
#pragma mark 數(shù)據(jù)源
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.dataArr.count;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CYWCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
cell.model = self.dataArr[indexPath.item];
return cell;
}
用懶加載的方式去加載plist中的數(shù)據(jù)
@interface ViewController ()
//懶加載之后的數(shù)據(jù)數(shù)組
@property (nonatomic,strong) NSMutableArray *dataArr;
@end
#pragma mark 懶加載
-(NSMutableArray *)dataArr{
if (_dataArr == nil) {
NSArray *arr = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1.plist" ofType:nil]];
NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:arr.count];
for (NSDictionary *dict in arr) {
CYWModel *model = [CYWModel modelWithDict:dict];
[arrM addObject:model];
}
_dataArr = arrM;
}
return _dataArr;
}
用來(lái)接受數(shù)據(jù)的模型
記得.h中要引入U(xiǎn)IKit框架,不然CGFloat是敲不出來(lái)的
#import <UIKit/UIKit.h>
@interface CYWModel : NSObject
//高
@property (nonatomic,assign) CGFloat height;
//寬
@property (nonatomic,assign) CGFloat width;
//圖片
@property (nonatomic,copy) NSString *icon;
//價(jià)格
@property (nonatomic,copy) NSString *price;
+(instancetype) modelWithDict:(NSDictionary *)dict;
@end
.m中最好實(shí)現(xiàn)下orUndefinedKey:方法,怕萬(wàn)一通過(guò)KVC沒(méi)找到對(duì)應(yīng)的key時(shí)候崩掉
#import "CYWModel.h"
@implementation CYWModel
+(instancetype)modelWithDict:(NSDictionary *)dict{
id obj = [[self alloc] init];
[obj setValuesForKeysWithDictionary:dict];
return obj;
}
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"%@ --- %@",value,key);
}
@end
接下來(lái)自定義的cell中引入模型屬性,重寫(xiě)set方法就好了。
@interface CYWCell ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *priceLabel;
@end
@implementation CYWCell
-(void)setModel:(CYWModel *)model{
_model = model;
self.imageView.image = [UIImage imageNamed:model.icon];
self.priceLabel.text = model.price;
}
接下來(lái)在模擬器中如果能運(yùn)行出如下圖的樣子就行了
示例
那么醉關(guān)鍵了位置就是接下來(lái)自定義我們的UICollectionViewFlowLayout
這是.h中留出來(lái)給控制器賦值的屬性,到時(shí)候你還可以自己添加你需要的屬性。
#import <UIKit/UIKit.h>
@interface CYWWaterFallLayout : UICollectionViewFlowLayout
//列數(shù)
@property (nonatomic,assign) NSInteger colCount;
//數(shù)據(jù)
@property (nonatomic,strong) NSArray *dataList;
@end
這是.m文件,關(guān)鍵的地方的注釋我也寫(xiě)上去了。
另外在插句嘴,其實(shí)瀑布流最主要就是限寬不限高去計(jì)算;另外,你以后在需要瀑布流布局的時(shí)候一定要記得找后臺(tái)的哥們把圖片的真實(shí)尺寸傳過(guò)來(lái),因?yàn)槟阈枰?jì)算長(zhǎng)寬比。#######
#import "CYWWaterFallLayout.h"
#import "CYWModel.h"
@interface CYWWaterFallLayout ()
//用來(lái)返回布局的數(shù)組
@property (nonatomic,strong) NSMutableArray *dataArr;
// 用來(lái)保存每一列item當(dāng)前的總高
@property (nonatomic, strong) NSMutableArray *eachColumnMaxHight;
@end
@implementation CYWWaterFallLayout
/*
* 準(zhǔn)備開(kāi)始布局 調(diào)用collectionView的 relodata方法也會(huì)調(diào)用這個(gè)方法
* 所以在這個(gè)方法里面自定義瀑布流的布局
*/
-(void)prepareLayout{
// 獲取collectionView中第0組的item個(gè)數(shù)
NSInteger itemNum= [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < itemNum; i++) {
NSIndexPath *indexpath = [NSIndexPath indexPathForItem:i inSection:0];
// 布局
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexpath];
// 總寬
CGFloat contentW = self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right;
// item寬
CGFloat itemW = (contentW - (self.colCount - 1) * self.minimumInteritemSpacing) / self.colCount;
// 獲取item高
CYWModel *model = self.dataList[i];
CGFloat itemH = itemW * (model.height /model.width);
// itemX
NSInteger colNum = i % self.colCount;
CGFloat itemX = self.sectionInset.left + (itemW + self.minimumInteritemSpacing) * colNum;
// itemY
CGFloat itemY = [self.eachColumnMaxHight[colNum] floatValue];
attr.frame = CGRectMake(itemX, itemY, itemW, itemH);
// 重新給數(shù)組中的最高y賦值
self.eachColumnMaxHight[colNum] = @(itemY + itemH + self.minimumLineSpacing);
[self.dataArr addObject:attr];
}
}
//返回collectioView的滾動(dòng)范圍
-(CGSize)collectionViewContentSize{
NSInteger maxCol = [self calculateMaxHeightCol];
return CGSizeMake(0, [self.eachColumnMaxHight[maxCol] floatValue] - self.minimumLineSpacing);
}
-(NSInteger)calculateMaxHeightCol{
NSInteger maxCol = 0;
CGFloat maxHeight = 0;
for (NSInteger i = 0; i < self.colCount; i++) {
if (maxHeight < [self.eachColumnMaxHight[i] floatValue]) {
maxHeight = [self.eachColumnMaxHight[i] floatValue];
maxCol = i;
}
}
return maxCol;
}
//這個(gè)方法中可以返回collectionView上所用item的索引,rect
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.dataArr;
}
#pragma mark 懶加載
-(NSMutableArray *)dataArr{
if (_dataArr == nil) {
_dataArr = [NSMutableArray array];
}
return _dataArr;
}
- (NSMutableArray *)eachColumnMaxHight {
if (_eachColumnMaxHight == nil) {
// 初始化可變數(shù)組
_eachColumnMaxHight = [NSMutableArray arrayWithCapacity:self.colCount];
// 給數(shù)組中的中賦值初始值
for (NSInteger i = 0; i < self.colCount; i++) {
// 讓每一列當(dāng)前的高度為一個(gè)組的頂部間距
_eachColumnMaxHight[i] = @(self.sectionInset.top);
}
}
return _eachColumnMaxHight;
}
@end