你的位置:首页 > 操作系统

[操作系统]自定义UICollectionViewLayout之瀑布流


目标效果


主视图文件,主要用于处理数据和布局页面

 

RootCell.h

1 #import <UIKit/UIKit.h>2 3 @interface RootCell : UICollectionViewCell4 5 @property (nonatomic, strong) UIImageView *photoView;6 7 @end

RootCell.m

 1 #import "RootCell.h" 2  3 @implementation RootCell 4  5 - (instancetype)initWithFrame:(CGRect)frame { 6   self = [super initWithFrame:frame]; 7   if (self) { 8     self.photoView = [[UIImageView alloc] init]; 9     [self.contentView addSubview:self.photoView];10   }11   return self;12 }13 14 - (void)layoutSubviews {15   16   self.photoView.frame = self.bounds;17   18 }19 20 @end

RootCell就是每一个Item的样式, 也就是一张张图片

 

WaterFlowLayout.h

 1 #import <UIKit/UIKit.h> 2  3 @protocol WaterFlowLayoutDelegate <NSObject> 4  5 // 返回每一个item的高度 6 - (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath; 7  8 @end 9 10 @interface WaterFlowLayout : UICollectionViewLayout11 12 // item的大小,需要根据这个获取宽度13 @property (nonatomic, assign) CGSize itemSize;14 15 // 内边距的设置16 @property (nonatomic, assign) UIEdgeInsets sectionInsets;17 18 // item的间距(这里水平方向和垂直方向的间距一样)19 @property (nonatomic, assign) CGFloat spacing;20 21 // 列数22 @property (nonatomic, assign) NSInteger numberOfColumn;23 24 // 设置代理,用于获取item的高度25 @property (nonatomic, weak) id<WaterFlowLayoutDelegate>delegate;26 27 @end

WaterFlowLayout.m

 1 #import "WaterFlowLayout.h" 2  3 @interface WaterFlowLayout () 4  5 // 声明私有属性 6 // 保存一共有多少个item 7 @property (nonatomic, assign) NSInteger numberOfItems; 8  9 // 保存计算好的每一个item的信息 10 @property (nonatomic, strong) NSMutableArray *itemAttributes; 11  12 // 保存每一列的高度 13 @property (nonatomic, strong) NSMutableArray *columnHeights; 14  15  16 // 声明私有方法 17 // 找到当前最长列 18 - (NSInteger)indexOfHeightestColumn; 19  20 // 找到当前最短列 21 - (NSInteger)indexOfShortestColumn; 22  23 @end 24  25 @implementation WaterFlowLayout 26  27 // 懒加载 28 - (NSMutableArray *)itemAttributes { 29   if (!_itemAttributes) { 30     _itemAttributes = [NSMutableArray array]; 31   } 32   return _itemAttributes; 33 } 34  35 - (NSMutableArray *)columnHeights { 36   if (!_columnHeights) { 37     _columnHeights = [NSMutableArray array]; 38   } 39   return _columnHeights; 40 } 41  42 // 找到当前最长列 43 - (NSInteger)indexOfHeightestColumn { 44   // 记录最长列的下标 45   NSInteger index = 0; 46   // 记录最长列的高度 47   CGFloat length = 0; 48   for (int i = 0; i < self.columnHeights.count; i++) { 49     // 将数组中的对象转为基本数值 50     CGFloat currentLength = [self.columnHeights[i] floatValue]; 51     if (currentLength > length) { 52       length = currentLength; 53       index = i; 54     } 55   } 56   return index; 57 } 58  59 // 找到当前最短列 60 - (NSInteger)indexOfShortestColumn { 61   NSInteger index = 0; 62   CGFloat length = MAXFLOAT; 63   for (int i = 0; i < self.columnHeights.count; i++) { 64      65     CGFloat currentLength = [self.columnHeights[i] floatValue]; 66     if (currentLength < length) { 67       length = currentLength; 68       index = i; 69     } 70   } 71   return index; 72 } 73  74 // 接下来重写三个方法 75  76 // 准备布局,在这里计算每个item的frame 77 - (void)prepareLayout { 78    79   // 拿到一共有多少个item 80   self.numberOfItems = [self.collectionView numberOfItemsInSection:0]; 81   // 每一列添加一个top高度 82   for (int i = 0; i < self.numberOfColumn; i++) { 83     // @() NSNumber字面量创建对象 84     self.columnHeights[i] = @(self.sectionInsets.top); 85   } 86    87   // 依次为每个item设置位置信息,并存储在数组中 88   for (int i = 0; i < self.numberOfItems; i++) { 89      90     // 1.找到最短列的下标 91     NSInteger shortestIndex = [self indexOfShortestColumn]; 92     // 2.计算X 目标X = 内边距左间距 + (宽 + item间距)*最短列下标 93     CGFloat detalX = self.sectionInsets.left + shortestIndex * (self.itemSize.width + self.spacing); 94     // 3.找到最短列的高度 95     CGFloat height = [self.columnHeights[shortestIndex] floatValue]; 96     // 4.计算Y 97     CGFloat detalY = height + self.spacing; 98     // 5.创建indexPath 99     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];100     101     // 6.调用代理方法计算高度102     CGFloat itemHeight = 0;103     if (_delegate && [_delegate respondsToSelector:@selector(heightForItemAtIndexPath:)]) {104       itemHeight = [_delegate heightForItemAtIndexPath:indexPath];105     }106     107     // 定义保存位置信息的对象108     UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];109     110     // 7.生成frame111     attributes.frame = CGRectMake(detalX, detalY, self.itemSize.width, itemHeight);112     // 8.将位置信息添加到数组中113     [self.itemAttributes addObject:attributes];114     115     // 9.更新这一列的高度116     self.columnHeights[shortestIndex] = @(detalY + itemHeight);117   }118   119 }120 121 // 返回UICollectionView的大小122 - (CGSize)collectionViewContentSize {123   124   // 求最高列的下标125   NSInteger heightest = [self indexOfHeightestColumn];126   // 最高列的高度127   CGFloat height = [self.columnHeights[heightest] floatValue];128   // 拿到collectionView的原始大小129   CGSize size = self.collectionView.frame.size;130   size.height = height + self.sectionInsets.bottom;131   132   return size;133 }134 135 // 返回每一个item的位置信息136 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {137   return self.itemAttributes;138 }139 140 @end

WaterFlowLayout 就是我们自定义的 瀑布流 的文件

 

DataModel.h

 1 #import <Foundation/Foundation.h> 2  3 @interface DataModel : NSObject 4  5 @property (nonatomic, copy) NSString *thumbURL; 6  7 @property (nonatomic, assign) float width; 8  9 @property (nonatomic, assign) float height;10 11 12 @end

DataModel.m

 1 #import "DataModel.h" 2  3 @implementation DataModel 4  5 // 防崩 6 - (void)setValue:(id)value forUndefinedKey:(NSString *)key { 7    8 } 9 10 @end