一颤难、瀑布流設計方案
二、瀑布流設計思路分析
- 1已维、自定義流水布局中行嗤,指定滾動方向、默認列數(shù)垛耳、行間距栅屏、列間距、以及指定cell的大小itemSize
- 2堂鲜、可以提供一個數(shù)組columnMaxYs(記錄當前每一列的最大Y值)栈雳,假如3列,我們就提供一個3個元素的數(shù)組缔莲,記錄所有布局屬性
- columnMaxYs實現(xiàn)懶加載甫恩,
- 2.并在prepareLayout方法中 :
- 1.先將columnMaxYs清空。
- 2.再進行初始化每個元素為0.
- 3.獲取所有的Cell的布局屬性酌予,而每一個Cell的布局屬性通過調(diào)用layoutAttributesForItemAtIndexPath:方式獲取磺箕,而調(diào)用該方法奖慌,就會執(zhí)行第4步
(至于為什么在prepareLayout方法中初始化,而不是在init方法初始化:
是因為松靡,該方法每次刷新都會調(diào)用简僧,而init方法中只會在創(chuàng)建布局對象的時候只執(zhí)行一次,
例如:如果進行下拉刷新最新數(shù)據(jù)的時候雕欺,需求重新初始化數(shù)據(jù)岛马,而如果我們使用的是init方法的話,并不會調(diào)用也就并不會清除之前的然后初始化屠列,
而使用該方法prepareLayout可以辦到啦逆,因為它會再次調(diào)用,而init不會調(diào)用)
```
+ 3.獲取所有Cell的布局屬性笛洛,
注意:對與所有Cell的布局屬性夏志,在第一次加載的時候需要計算一次,而當刷新collectionView的時候苛让,當然也需要重新計算:所以我們提供一個布局屬性的數(shù)組沟蔑,來保存所有Cell的布局刷新,避免不必要的計算狱杰。
——> 放在哪瘦材? 計算最好呢?
———> 首選放在prepareLayout方法中仿畸,因為它會調(diào)用一次加載食棕,而且當刷新的時候也會調(diào)用,滿足需求错沽,我們先將之前的全部移除宣蠕,然后重新返回最新的布局屬性數(shù)組; 但是甥捺,最好不要放在layoutAttributesForElementsInRect:方法(返回所有元素的布局屬性數(shù)組中)抢蚀,因為改方法在滾動collectionView的時候,會頻繁的調(diào)用镰禾,比較銷毀性能皿曲。
-
3、在layoutAttributesForElementsInRect:方法(返回所有元素的布局屬性數(shù)組 )
- 返回之前保存的所有Cell的布局屬性數(shù)組
4吴侦、我們可以在layoutAttributesForItemAtIndexPath: 方法: 來調(diào)整 Cell的布局屬性 屋休, 指定Cell的 frame
1.在該方法中拿到當前cell的默認布局屬性attrs,進行下面的調(diào)整备韧,就可以實現(xiàn)瀑布流了
2.遍歷columnMaxYs數(shù)組劫樟,需要找出最短一列的 列號 與 最大Y值
3. 確定當前Cell的 存放的x.y位置 以及widht與height
—> width:根據(jù)屏幕寬度與列數(shù)以及每列的寬度求出. height:服務器返回的
—> x:需要根據(jù)當前最短一列的列號與Cell的寬度與間距可以求出來;y : 可以根據(jù)當前最短一列的最大Y值 + 行間距可以求出
4.重新設置修改當前Cell的布局屬性attrs 的 frame即可〉蓿—> 之前已經(jīng)拿到當前Cell的 默認布局屬性奶陈,以及上一步已經(jīng)獲取到當前Cell需要存放的x.y位置后,
5.將修改Cell布局屬性之后的附较,當前列當前Cell的Y值最大吃粒,所有我們要將該值記錄到數(shù)組columnMaxYs中,以便下次對比
```
5拒课、注意:我們需要設置collectionView的contentSize徐勃,它才會滾動,那么我們?nèi)绾卧O置呢早像?
——> 在定義布局類中僻肖,系統(tǒng)提供了一個collectionViewContentSize的get對象方法(決定collectionView的contentSize)
—> contentSize的高度為:計算出最長那一列的最大Y值,也就是columnMaxYs的最大值 + 行間距
```
三卢鹦、瀑布流的基本實現(xiàn)
- 1.創(chuàng)建一個控制器JPCollectionViewController臀脏,繼承UICollectionViewController,使用我們自定義的流水布局JPWaterflowLayout(實現(xiàn)見其后)
#import "JPCollectionViewController.h"
#import "JPWaterflowLayout.h" // 自定義流水布局
@interface JPCollectionViewController ()
@end
@implementation JPCollectionViewController
static NSString * const reuseIdentifier = @"cellID";
- (void)viewDidLoad {
[super viewDidLoad];
// 切換布局
self.collectionView.collectionViewLayout = [[JPWaterflowLayout alloc] init];
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor orangeColor];
return cell;
}
@end
- 2.自定義流水布局JPWaterflowLayout法挨,繼承UICollectionViewLayout
#import "JPWaterflowLayout.h"
#define JPCollectionW self.collectionView.frame.size.width
/** 每一行之間的間距 */
static const CGFloat JPDefaultRowMargin = 10;
/** 每一列之間的間距 */
static const CGFloat JPDefaultColumnMargin = 10;
/** 每一列之間的間距 top, left, bottom, right */
static const UIEdgeInsets JPDefaultInsets = {10, 10, 10, 10};
/** 默認的列數(shù) */
static const int JPDefaultColumsCount = 3;
@interface JPWaterflowLayout()
/** 每一列的最大Y值 */
@property (nonatomic, strong) NSMutableArray *columnMaxYs;
/** 存放所有cell的布局屬性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end
@implementation JPWaterflowLayout
#pragma mark - 懶加載
- (NSMutableArray *)columnMaxYs
{
if (!_columnMaxYs) {
_columnMaxYs = [[NSMutableArray alloc] init];
}
return _columnMaxYs;
}
- (NSMutableArray *)attrsArray
{
if (!_attrsArray) {
_attrsArray = [[NSMutableArray alloc] init];
}
return _attrsArray;
}
#pragma mark - 實現(xiàn)內(nèi)部的方法
/**
* 決定了collectionView的contentSize
*/
- (CGSize)collectionViewContentSize
{
// 找出最長那一列的最大Y值
CGFloat destMaxY = [self.columnMaxYs[0] doubleValue];
for (NSUInteger i = 1; i<self.columnMaxYs.count; i++) {
// 取出第i列的最大Y值
CGFloat columnMaxY = [self.columnMaxYs[i] doubleValue];
// 找出數(shù)組中的最大值
if (destMaxY < columnMaxY) {
destMaxY = columnMaxY;
}
}
return CGSizeMake(0, destMaxY + JPDefaultInsets.bottom);
}
- (void)prepareLayout
{
[super prepareLayout];
// 重置每一列的最大Y值
[self.columnMaxYs removeAllObjects];
for (NSUInteger i = 0; i<JPDefaultColumsCount; i++) {
[self.columnMaxYs addObject:@(JPDefaultInsets.top)];
}
// 計算所有cell的布局屬性
[self.attrsArray removeAllObjects];
NSUInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSUInteger i = 0; i < count; ++i) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attrsArray addObject:attrs];
}
}
/**
* 說明所有元素(比如cell谁榜、補充控件幅聘、裝飾控件)的布局屬性
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
/**
* 說明cell的布局屬性
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
/** 計算indexPath位置cell的布局屬性 */
// 水平方向上的總間距
CGFloat xMargin = JPDefaultInsets.left + JPDefaultInsets.right + (JPDefaultColumsCount - 1) * JPDefaultColumnMargin;
// cell的寬度
CGFloat w = (JPCollectionW - xMargin) / JPDefaultColumsCount;
// cell的高度凡纳,測試數(shù)據(jù),隨機數(shù)
CGFloat h = 50 + arc4random_uniform(150);
// 找出最短那一列的 列號 和 最大Y值
CGFloat destMaxY = [self.columnMaxYs[0] doubleValue];
NSUInteger destColumn = 0;
for (NSUInteger i = 1; i<self.columnMaxYs.count; i++) {
// 取出第i列的最大Y值
CGFloat columnMaxY = [self.columnMaxYs[i] doubleValue];
// 找出數(shù)組中的最小值
if (destMaxY > columnMaxY) {
destMaxY = columnMaxY;
destColumn = i;
}
}
// cell的x值
CGFloat x = JPDefaultInsets.left + destColumn * (w + JPDefaultColumnMargin);
// cell的y值
CGFloat y = destMaxY + JPDefaultRowMargin;
// cell的frame
attrs.frame = CGRectMake(x, y, w, h);
// 更新數(shù)組中的最大Y值
self.columnMaxYs[destColumn] = @(CGRectGetMaxY(attrs.frame));
return attrs;
}
@end