在實現瀑布流之前先來看看瀑布流的雛形(此方法的雛形 UICollectionView)
對於UICollectionView我們有幾點注意事項
圖(一)

如圖,模擬器上展示的是很多方格,但是值得注意的是他們是有規則的。
雖然看上去很整潔但是並不美觀。
我們所說的要實現瀑布流就是要實現它的不整潔,但是規律(這裡我說的是規律)
前面說了UIcollectionView的大部分操作在FlowLayout上,當然也包括格局部署。
為了實現瀑布流我們所要實現的便是改變他的格局部署。

下面是實現的代碼部分(不提供demo了 很簡單)
我在注釋中簡單介紹。
---
//
// ViewController.m
// CX-瀑布流UIcollectionView實現
//
// Created by ma c on 16/4/8.
// Copyright © 2016年 bjsxt. All rights reserved.
//
#import "ViewController.h"
#import "CXCollectionViewCell.h"
#import "CXCollectionViewLayout.h"
static NSString * identifier = @"cellID";
@interface ViewController ()<UICollectionViewDataSource>
//所要展示的UICollectionView
@property (nonatomic, strong) UICollectionView * collectionView;
@end
@implementation ViewController
#pragma mark - <懶加載>
- (UICollectionView *)collectionView {
if (!_collectionView) {
//初始化我們自定義的flowLayout
CXCollectionViewLayout * flowLayout = [[CXCollectionViewLayout alloc]init];
//初始化collectionView
_collectionView = [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
//設置數據源(collectionView的命根子)
_collectionView.dataSource = self;
//注冊我們自定義的cell
[_collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CXCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:identifier];
}
return _collectionView;
}
#pragma mark - <life>
- (void)viewDidLoad {
[super viewDidLoad];
//在self.view上添加---
[self.view addSubview:self.collectionView];
}
#pragma mark - <UICollectionViewDataSource>
//這裡返回的是item的個數 返回100
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 100;
}
//這裡返回的是cell 我們可以在這裡進行一些簡單的操作
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CXCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
//為了瀑布流的實現細節我們添加的Label
cell.label.text = [NSString stringWithFormat:@"%zd",indexPath.item];
//cell的背景色
cell.backgroundColor = [UIColor orangeColor];
return cell;
}
@end
---
//
// CXCollectionViewLayout.m
// CX-瀑布流UIcollectionView實現
//
// Created by ma c on 16/4/8.
// Copyright © 2016年 bjsxt. All rights reserved.
//
#import "CXCollectionViewLayout.h"
//瀑布流的列數
static NSInteger CXcolumnCount = 3;
//瀑布流的內邊距
static UIEdgeInsets CXdefaultEdgeInsets = {20,15,10,15};
//cell的列間距
static NSInteger CXcolumnMagin = 10;
//cell的行間距
static NSInteger CXrowMagin = 10;
@interface CXCollectionViewLayout ()
//存放所有cell 的布局屬性
@property (nonatomic, strong) NSMutableArray * CXattrsArray;
//縮放所有列的高度
@property (nonatomic, strong) NSMutableArray * CXcolumnHeights;
@end
@implementation CXCollectionViewLayout
#pragma mark - <懶加載>
- (NSMutableArray *)CXattrsArray{
if (!_CXattrsArray) {
_CXattrsArray = [NSMutableArray array];
}
return _CXattrsArray;
}
- (NSMutableArray *)CXcolumnHeights{
if (!_CXcolumnHeights) {
_CXcolumnHeights = [NSMutableArray array];
}
return _CXcolumnHeights;
}
#pragma mark - <准備布局>
//准備布局(布局前自動執行)
- (void) prepareLayout{
//重寫此方法一定要記得super
[super prepareLayout];
//在實際操作中我們的數據並不會固定不變的,因此我們每次布局前最好要清空之前存儲的屬性
//清空存放所有列的高度
//清空存放所有cell的不去屬性
[self.CXcolumnHeights removeAllObjects];
[self.CXattrsArray removeAllObjects];
//首先為第一行的cell附高度
for (NSInteger i = 0; i < CXcolumnCount; i ++) {
//數組裡只能存放對象
[self.CXcolumnHeights addObject:@(CXdefaultEdgeInsets.top)];
}
//下面開始創建每一個cell的布局屬性 並且添加到存儲cell布局屬性的數組中
//cell總個數 因為這裡只要一個section
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < count; i ++) {
// 創建位置 即indexPath
NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
//獲取indexPath對應的cell布局屬性
UICollectionViewLayoutAttributes * attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
//把獲取到的布局屬性添加到數組中
[self.CXattrsArray addObject:attributes];
}
//准備布局的工作到這裡就結束了
}
//返回所有cell布局屬性 及整體cell 的排布
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.CXattrsArray;
}
//返回cell 的布局屬性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
//創建布局屬性
UICollectionViewLayoutAttributes * CXattributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//獲取collectionView 的寬
CGFloat collectionViewWidth = self.collectionView.frame.size.width;
//下面的一部分是獲取cell的frame(布局屬性)
CGFloat width;
CGFloat height;
CGFloat X;
CGFloat Y;
//獲取width
width = (collectionViewWidth - CXdefaultEdgeInsets.left - CXdefaultEdgeInsets.right - (CXcolumnCount - 1) * CXcolumnMagin) / CXcolumnCount;
//獲取height
//在實際開發中heigh並不是真正的隨機 而是根據數據來決定height 在這裡展示初步的介紹其原理 因此采用大於100小於150的隨機數
height = 100 + arc4random_uniform(50);
//獲取X (瀑布流的實現重點就在cell的X,Y值獲取)
//設置一個列數的中間變量
NSInteger tempColumn = 0;
//設置高度小的中間變量 在這裡我們把第0列的高度給他,這樣可以減少循環次數,提高效率
CGFloat minColumnHeight = [self.CXcolumnHeights[0] doubleValue];
for (NSInteger i = 1; i < CXcolumnCount; i ++) {
if (minColumnHeight > [self.CXcolumnHeights[i] doubleValue]) {
minColumnHeight = [self.CXcolumnHeights[i] doubleValue];
tempColumn = i;
}
}
X = CXdefaultEdgeInsets.left + (width + CXcolumnMagin) * tempColumn;
//獲取Y
Y = minColumnHeight;
if (Y != CXdefaultEdgeInsets.top) {
Y += CXrowMagin;
}
//設置cell的frame
CXattributes.frame = CGRectMake(X, Y, width, height);
//更新高度最矮的那列的高度
self.CXcolumnHeights[tempColumn] = @(CGRectGetMaxY(CXattributes.frame));
return CXattributes;
}
//返回collegeView的Content的大小
- (CGSize)collectionViewContentSize{
//雖說返回的是大小,但是我們這裡主要的是height
CGFloat maxColumnHeight = [self.CXcolumnHeights[0] doubleValue];
for (NSInteger i = 1; i < CXcolumnCount; i++) {
CGFloat columnHeight = [self.CXcolumnHeights[i] doubleValue];
if (maxColumnHeight < columnHeight) {
maxColumnHeight = columnHeight;
}
}
return CGSizeMake(0, maxColumnHeight + CXdefaultEdgeInsets.bottom);
}
@end
到此為止瀑布流的實現也就結束了。
在這裡說明幾點值得注意的地方。