iOS開發-進階:瀑布流基本實現
阿新 • • 發佈:2019-02-08
文章轉自: http://www.jianshu.com/p/78830bdb04a9
一、瀑布流設計方案
不可取.png
過於複雜.png
最優方案.png
二、瀑布流設計思路分析
- 1、自定義流水佈局中,指定滾動方向、預設列數、行間距、列間距、以及指定cell的大小itemSize
-
2、可以提供一個數組columnMaxYs(記錄當前每一列的最大Y值),假如3列,我們就提供一個3個元素的陣列,記錄所有佈局屬性
-
- columnMaxYs實現懶載入,
- 2.並在prepareLayout方法中 :
- 1.先將columnMaxYs清空。
- 2.再進行初始化每個元素為0.
- 3.獲取所有的Cell的佈局屬性,而每一個Cell的佈局屬性通過呼叫layoutAttributesForItemAtIndexPath:方式獲取,而呼叫該方法,就會執行第4步
(至於為什麼在prepareLayout方法中初始化,而不是在init
- 3.獲取所有Cell的佈局屬性,
注意:對與所有Cell的佈局屬性,在第一次載入的時候需要計算一次,而當重新整理collectionView的時候,當然也需要重新計算:所以我們提供一個佈局屬性的陣列,來儲存所有Cell的佈局重新整理,避免不必要的計算。 ——> 放在哪? 計算最好呢? ———> 首選放在prepareLayout方法中,因為它會呼叫一次載入,而且當重新整理的時候也會呼叫,滿足需求,我們先將之前的全部移除,然後重新返回最新的佈局屬性陣列; 但是,最好不要放在layoutAttributesForElementsInRect:方法(返回所有元素的佈局屬性陣列中),因為改方法在滾動collectionView的時候,會頻繁的呼叫,比較銷燬效能。
-
-
3、在layoutAttributesForElementsInRect:方法(返回所有元素的佈局屬性陣列 )
- 返回之前儲存的所有Cell的佈局屬性陣列
-
4、我們可以在layoutAttributesForItemAtIndexPath: 方法: 來調整 Cell的佈局屬性 , 指定Cell的 frame
1.在該方法中拿到當前cell的預設佈局屬性attrs,進行下面的調整,就可以實現瀑布流了 2.遍歷columnMaxYs陣列,需要找出最短一列的 列號 與 最大Y值 3. 確定當前Cell的 存放的x.y位置 以及widht與height —> width:根據螢幕寬度與列數以及每列的寬度求出. height:伺服器返回的 —> x:需要根據當前最短一列的列號與Cell
5、注意:我們需要設定collectionView的contentSize,它才會滾動,那麼我們如何設定呢?
——> 在定義佈局類中,系統提供了一個collectionViewContentSize的get物件方法(決定collectionView的contentSize)
—> contentSize的高度為:計算出最長那一列的最大Y值,也就是columnMaxYs的最大值 + 行間距
三、瀑布流的基本實現
效果圖.png
- 1.建立一個控制器JPCollectionViewController,繼承UICollectionViewController,使用我們自定義的流水佈局JPWaterflowLayout(實現見其後)
#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};
/** 預設的列數 */
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 - 實現內部的方法
/**
* 決定了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];
// 找出陣列中的最大值
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的高度,測試資料,隨機數
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];
// 找出陣列中的最小值
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);
// 更新陣列中的最大Y值
self.columnMaxYs[destColumn] = @(CGRectGetMaxY(attrs.frame));
return attrs;
}
@end