UIColletionView瀑布流佈局實現思路以及封裝的實現
瀑布流實現思路
- 第一種就是用ScrollView來進行實現,由於它不具備複用的功能,因此我們需要自己寫一套類似複用的模組來進行優化
- 第二種就是利用apple做好的複用模組,自定義UIColletionLayout來實現瀑布流,想想也是第二種實現起來更快更優,OK,封裝一個小小的框架來試試
其他案例
上面的動畫切換佈局也是自定義UICollectionLayout來進行佈局的,簡單的靜態圖片佈局展示其實就重寫幾個方法就可以了
1.prepareLayout
每次重新重新整理collectionView的時候會呼叫一次,做一些初始化的工作
2.layoutAttributesForElementsInRect
返回已經制定好之後的每個cell對應的attribute屬性物件進行佈局
3.layoutAttributesForItemAtIndexPath
該方法會一直呼叫,每次cell出來就會根據對應的indexpath來進行方法呼叫,因此關鍵佈局程式碼就可以放置在這裡進行重新計算
4.collectionViewContentSize
計算整體的大小,實現滾動
瀑布流實現分析
1.基本變數的宣告
// 每一列的間距
static const CGFloat MKJDefaultColumnMargin = 10;
// 每一行間距
static const CGFloat MKJDefaultRowMargin = 10;
// 整體的上間距,左間距,下間距,右間距
static const UIEdgeInsets MKJDefaultEdgeInsets = {10,10,10,10};
// 預設是多少列
static const NSUInteger MKJDefaultColumnCounts = 2;
@interface MKJWaterFallLayout ()
@property (nonatomic,strong) NSMutableArray *attributeArr; // cell屬性的陣列
@property (nonatomic,strong) NSMutableArray *columnHeightArr; // 每列的高度陣列
@end
2.初始化
// 每次重新整理會呼叫一次
- (void)prepareLayout
{
[super prepareLayout];
// 每次重新重新整理的時候清除之前的所有高度值,預設就是UIEdg給定的top
[self.columnHeightArr removeAllObjects];
for (NSInteger i = 0; i < [self columnCount]; i++) {
[self.columnHeightArr addObject:@([self insetMargin].top)];
}
// 每次重新整理把對應的att屬性清空
[self.attributeArr removeAllObjects];
// 初始化一次每個cell對應的attribute屬性
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < count; i++) {
NSIndexPath *indexpath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:indexpath];
[self.attributeArr addObject:attribute];
}
}
3.關鍵計算程式碼
// 返回attribute屬性陣列決定最後的排布
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attributeArr;
}
// 返回對應的indexpath下每個cell的屬性 cell的出現會一直重新整理該方法
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
// 初始化佈局屬性---> 對應的indexpath
UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGFloat collectionW = self.collectionView.frame.size.width;
// 寬度是根據列數和間距固定算出來的
CGFloat width = (collectionW - [self insetMargin].left - [self insetMargin].right - ([self columnCount] - 1) * [self columnMargin]) / [self columnCount];
// 高度是根據代理的資料來源返回比例計算的
CGFloat height = [self.delegate MKJWaterFallLayout:self heightForItemAtIndexPath:indexPath] * width;
// X 和 Y值都是在找出最小column之後才能確定,核心就是根據列數,找出最小高度的那一列
// 先取出第一個預設是最小的
CGFloat minColumnHeight = [self.columnHeightArr[0] doubleValue];
// 預設最小的是第0列
NSUInteger finalCol = 0;
for (NSInteger i = 1 ; i < [self columnCount]; i++) {
CGFloat currentColHeight = [self.columnHeightArr[i] doubleValue];
if (minColumnHeight > currentColHeight) {
minColumnHeight = currentColHeight;
finalCol = i;
}
}
// x,y值是根據最小高度列算出來的
CGFloat x = [self insetMargin].left + (width + [self columnMargin]) * finalCol;
CGFloat y = minColumnHeight;
// 當你是一個行排布的時候 預設是top值,不需要加間距
NSInteger count = indexPath.item;
if ((count / ([self columnCount])) >= 1) {
y += [self rowMargin];
}
att.frame = CGRectMake(x, y, width, height);
self.columnHeightArr[finalCol] = @(CGRectGetMaxY(att.frame));
return att;
}
這裡的計算簡概括為就是對每個cell進行frame的計算
1.寬度的計算是根據列間距和整體左右間距以及行數進行限制,這些引數可以是固定值,也可以是代理傳進去的
2.高度的計算必定是根據外部代理進行計算的
3.X值的計算是根據這個框架內部的每一列的高度陣列進行之前的快取高度,進行最小值計算,然後拿出最小值對應的列數,根據上面算出來的高度進行X值的計算
4.Y值的計算和X值一樣,根據給定的陣列,比出最小高度列的列數,根據陣列的高度,計算出對應的Y值,最終再進行陣列對應列數的高度更新
4.獲取實際能容大小,讓其可以滾動
// 計算出滾動區域的大小
- (CGSize)collectionViewContentSize
{
CGFloat maxColumHeight = [self.columnHeightArr[0] doubleValue];
for (NSInteger i = 1; i < [self columnCount]; i++) {
CGFloat currentColHeight = [self.columnHeightArr[i] doubleValue];
if (maxColumHeight < currentColHeight) {
maxColumHeight = currentColHeight;
}
}
return CGSizeMake(0, maxColumHeight + [self insetMargin].bottom);
}
5.這個小框架已經封裝好了,簡單介紹下用法
// 宣告如下
- (UICollectionView *)colletionView
{
if (_colletionView == nil) {
MKJWaterFallLayout *layout = [[MKJWaterFallLayout alloc] init];
layout.delegate = self;
UICollectionView *collectionV = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
collectionV.backgroundColor = [UIColor redColor];
[collectionV registerNib:[UINib nibWithNibName:productID bundle:nil] forCellWithReuseIdentifier:productID];
collectionV.delegate = self;
collectionV.dataSource = self;
_colletionView = collectionV;
}
return _colletionView;
}
// 內部行數,間距的控制
#pragma mark - waterfallLayoutDelegate
- (CGFloat)MKJWaterFallLayout:(MKJWaterFallLayout *)layout heightForItemAtIndexPath:(NSIndexPath *)indexPath
{
// 返回寬度和高度比例
MKJProductModel *product = self.dataSource[indexPath.item];
return product.h / product.w;
}
// 控制列間距
- (CGFloat)columnMarginForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return 10;
}
// 控制行間距
- (CGFloat)rowMarginForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return 30;
}
// 控制列數
- (NSUInteger)columnCountForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
// if (self.dataSource.count > 50) {
// return 3;
// }
return 3;
}
// 控制整體上左下右間距
- (UIEdgeInsets)insetForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
return UIEdgeInsetsMake(10, 10, 10, 10);
}
一個簡單的Demo以及一個框架就這樣基本設計完了,需要的朋友可以直接下載拖到自己的工程裡面試試,有問題請留言
Demo地址:
Demo和框架傳送門
相關文章
- 談談實現瀑布流佈局的幾種思路
- jQuery實現瀑布流佈局jQuery
- 原生 js 實現瀑布流佈局、React 版本的瀑布流佈局元件JSReact元件
- Bootstrap實戰 - 瀑布流佈局boot
- CSS3實現瀑布流佈局(display: flex/column-coCSSS3Flex
- 原生js實現圖片瀑布流佈局,註釋超詳細JS
- css實現瀑布流CSS
- 卡片瀑布流實現
- vue實現瀑布流Vue
- flutter瀑布流佈局Flutter
- 瀑布流簡單實現
- 瀑布流程式碼實現及思路
- 淺析瀑布流佈局原理
- 記錄:瀑布流最佳實現方案
- CSS3 column 瀑布流佈局CSSS3
- 純CSS實現瀑布流,你會嗎?CSS
- 使用 yogaKit 實現一個資訊流佈局
- CSS佈局–聖盃佈局和雙飛翼佈局以及使用Flex實現聖盃佈局CSSFlex
- 佈局總結-水平居中佈局的實現
- Grid 拖拽佈局實現
- 節流原理以及實現
- 談談 Promise 以及實現 Fetch 的思路Promise
- 在鴻蒙中實現類似瀑布流效果鴻蒙
- vue全家桶仿某魚部分佈局以及功能實現Vue
- 瀑布 敏捷與現實敏捷
- Material Design之RecyclerView基本講解與瀑布流的實現Material DesignView
- Flutter學習:什麼是Container以及佈局約束的實現FlutterAI
- 聖盃佈局進階版-flex佈局實現Flex
- css佈局-實現左中右佈局的5種方式CSS
- Vben-Admin的useForm實現思路詳解,以及實現element版的useFormORM
- 微信小程式實戰,基於vue2實現瀑布流微信小程式Vue
- Go + AideLua 實現雲端佈局GoAIIDE
- div 實現田字格佈局
- CSS Grid實現聖盃佈局CSS
- Flutter簡單實現手寫瀑布流 第二篇Flutter
- 一種子圖佈局方法的實現
- 實現三欄佈局的幾種方法
- Vue實現跑馬燈效果以及封裝為元件釋出Vue封裝元件
- 使用 CSS columns 佈局來實現自動分組佈局CSS