看完千篇一律的UI布局之后,當我們看到瀑布流的布局是不是覺得有種耳目一新的感覺呢?今天我們就說一下如果實現瀑布流,對于瀑布流,現在iOS中總共存在著三種實現方法.
- 1.實現瀑布流的布局,我們需要計算每一張圖片的尺寸大小,然后根據列數布局到我們的UIScrollView上去
- 2.UITableView實現瀑布流效果,就是每一列都是一個視圖.
- 3.UICollectionView實現瀑布流就是對UICollectionView的UICollectionViewLayout重寫
瀑布流.gif
實現思想:
就是把UICollectionView分成三列,由數組保存每一列的高度,然后每次設置UICollectionViewLayoutAttributes的時候,獲取最短一列,計算出圖片的size,然后添加到最短一列上面。
具體代碼
自定義布局的實現.h
#import <UIKit/UIKit.h>
@class WaterLayout;
//協議
@protocol WaterLayoutDelegate <NSObject>
//代理方法 返回item的高度
- (CGFloat)waterLayout:(WaterLayout *)waterLayout heightForRowAtIndexPath:(NSInteger )index itemWidth:(CGFloat )itemWidth;
@end
@interface WaterLayout : UICollectionViewLayout
@property (nonatomic,assign) CGFloat columnMargin;//列間距
@property (nonatomic,assign) CGFloat rowMargin;//行間距
@property (nonatomic,assign) UIEdgeInsets edge;//邊緣間距
@property (nonatomic,assign) NSInteger columnCount;//列數
@property (nonatomic,assign) id<WaterLayoutDelegate> delegate;//代理
@end
自定義布局的實現.m
#import "WaterLayout.h"
@interface WaterLayout ()
@property (nonatomic,strong) NSMutableArray *attributesArray; //存放所有cell的布局屬性
@property (nonatomic,strong) NSMutableArray *allColumnMaxYArray; //所有列的高度
@property (nonatomic,assign) CGFloat contentSizeHeight;//內容的高度
@end
@implementation WaterLayout
- (NSMutableArray *)attributesArray {
if (!_attributesArray) {
_attributesArray = [NSMutableArray array];
}
return _attributesArray;
}
- (NSMutableArray *)allColumnMaxYArray {
if (!_allColumnMaxYArray) {
_allColumnMaxYArray = [NSMutableArray array];
}
return _allColumnMaxYArray;
}
- (void)prepareLayout {
[super prepareLayout];
self.contentSizeHeight = 0;
//先清空數組
[self.allColumnMaxYArray removeAllObjects];
for (NSInteger i = 0; i < self.self.columnCount; i++) {
[self.allColumnMaxYArray addObject:@(self.self.edge.top)];
}
//開始創建每一個cell對應的布局屬性
//一共有多少個cell
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (int i = 0; i < count; i++) {
//獲取i位置上的索引
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
//獲取每個cell的布局屬性
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
//添加
[self.attributesArray addObject:attributes];
}
}
/**
* 這個方法會多次調用
*/
//cell的排布
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
return self.attributesArray;
}
//indexPath對應cell的布局屬性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
//為這個cell創建布局屬性
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//collectionViewW
CGFloat collectionViewW = self.collectionView.frame.size.width;
//找出高度最短的那一列
NSInteger column = 0;
//默認第一列的高度最短
CGFloat minColumnHeight = [self.allColumnMaxYArray[0] doubleValue];
//遍歷數組所有值
for (NSInteger i = 1; i < self.self.columnCount; i++) {
//取出每一列的高度
CGFloat columnHeight = [self.allColumnMaxYArray[i] doubleValue];
//判斷高度
if (minColumnHeight >columnHeight) {
minColumnHeight = columnHeight;
//最短列賦值給columnY
column = i;
}
}
//設置布局
CGFloat w = (collectionViewW - self.edge.left - self.edge.right - (self.columnCount - 1)*self.self.columnMargin) / self.columnCount;
CGFloat x = self.edge.left + column * (w + self.columnMargin);
CGFloat y = minColumnHeight;
//如果不是第一行時
if (y != self.edge.top) {
y += self.rowMargin;
}
//高度由外界決定,通過delegate
CGFloat h = [self.delegate waterLayout:self heightForRowAtIndexPath:indexPath.row itemWidth:w];
//設置frame
attributes.frame = CGRectMake(x, y, w, h);
//更新最短列的高度
self.allColumnMaxYArray[column] = @(CGRectGetMaxY(attributes.frame));
//記錄最大高度
CGFloat columHeight = [self.allColumnMaxYArray[column] doubleValue];
if (self.contentSizeHeight < columHeight) {
self.contentSizeHeight = columHeight;
}
return attributes;
}
//collectionView的ContentSize
- (CGSize)collectionViewContentSize {
//最大高度+self.edge底部高度
return CGSizeMake(0, self.contentSizeHeight+self.edge.bottom);
}
@end
外界使用布局方式
#import "ViewController.h"
#import "WaterLayout.h"
#import "ShopModel.h"
#import "ShopViewCell.h"
@interface ViewController ()<UICollectionViewDataSource,WaterLayoutDelegate>
@property (nonatomic,strong) NSMutableArray *shopModelArray;
@end
@implementation ViewController
- (NSMutableArray *)shopModelArray {
if (!_shopModelArray) {
_shopModelArray = [NSMutableArray array];
}
return _shopModelArray;
}
- (void)setData {
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1.plist" ofType:nil]];
for (NSDictionary *dic in array) {
ShopModel *shop = [[ShopModel alloc] init];
[shop setValuesForKeysWithDictionary:dic];
[self.shopModelArray addObject:shop];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
//加載數據
[self setData];
[self setLayout];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)setLayout {
WaterLayout *layout = [[WaterLayout alloc] init];
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
collectionView.backgroundColor = [UIColor whiteColor];
layout.delegate = self;
layout.columnCount = 3;
layout.columnMargin = 10;
layout.rowMargin = 10;
layout.edge = UIEdgeInsetsMake(10, 10, 10, 10);
collectionView.dataSource =self;
[self.view addSubview:collectionView];
[collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([ShopViewCell class]) bundle:nil] forCellWithReuseIdentifier:@"shop"];
}
- (NSInteger )numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.shopModelArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShopViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"shop" forIndexPath:indexPath];
[cell setCellBasicInfoWithModel:self.shopModelArray[indexPath.row]];
return cell;
}
#pragma mark -----waterLayoutDelegate
- (CGFloat)waterLayout:(WaterLayout *)waterLayout heightForRowAtIndexPath:(NSInteger)index itemWidth:(CGFloat)itemWidth {
ShopModel *shop = self.shopModelArray[index];
return itemWidth*shop.h/shop.w;
}