文章目录
- 自定义UICollectionViewLayout 流程
- 一 在 prepareLayout 创建布局属性
- 二、 重写 layoutAttributesForElementsInRect 在里面返回创建的布局属性数组
- 三、 重写 collectionViewContentSize方法,并在里面返回总内容的size
- 注意点
- 一、我们自定义layout 布局在创建属性布局的时候需要记录一个当前的origin
- 二、 如果我们自定义的layout 重写prepareLayout,则我们必须重写collectionViewContentSize 方法
- 三 、 自定义layout 中系统不支持直接获取某个item的size ,需要我们自定义协议并设置代理获取
- 四、 总结起来就是 : 每个item frame 的origin 需要layout 自己记录维护,size 需要通过代理获取
- 完整代码
- .h
- .m
自定义UICollectionViewLayout 流程
一 在 prepareLayout 创建布局属性
//创建layout类,继承于系统类 UICollectionViewFlowLayout
@interface TPShareCollectionViewFlowLayout : UICollectionViewFlowLayout
@end
/// 重写 prepareLayout 方法,并在该方法里面创建布局属性
- (void)prepareLayout
{
[super prepareLayout];
[self configLayout];
}
/// 创建布局的方法
- (void)configLayout
{
///添加了一个 layoutAttributesArray 数组,用来持有创建的布局属性,并在 layoutAttributesForElementsInRect 返回
[self.layoutAttributesArray removeAllObjects];
NSInteger count = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
CGFloat left = 0;
for (NSInteger i = 0; i < count ;i ++) {
// 创建布局属性
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
/// 这里是一个自定义的代理方法,通过改方法获取到相应item的size
CGSize size = [self.delegate collection:self.collectionView sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
CGRect frame = CGRectMake(left, 0, size.width, size.height);
layoutAttributes.frame = frame;
[self.layoutAttributesArray addObject:layoutAttributes];
if (i == count - 1) {
self.width = left + size.width;
} else {
left += (size.width + self.minimumLineSpacing);
}
}
}
二、 重写 layoutAttributesForElementsInRect 在里面返回创建的布局属性数组
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return self.layoutAttributesArray;
}
三、 重写 collectionViewContentSize方法,并在里面返回总内容的size
注意,我们需要在自定义layout 中管理记录当前布局的size,用来返回,因为我这里高度是固定的,所以自己记录的内容的宽度
即self.width 这个自定义属性
- (CGSize)collectionViewContentSize
{
return CGSizeMake(self.width, self.collectionView.bounds.size.height);
}
注意点
一、我们自定义layout 布局在创建属性布局的时候需要记录一个当前的origin
因为我这里自定义的y是固定的,都是0(一个横向的自定义layout),所以只是维护记录了一个当前的x位置
即left
left += (size.width + self.minimumLineSpacing);
二、 如果我们自定义的layout 重写prepareLayout,则我们必须重写collectionViewContentSize 方法
**collectionViewContentSize 默认会根据系统的布局属性数组(我们不知道属性名字)layout. 自身的总size **
不重写collectionViewContentSize 方法的话,layout 还是使用系统的数组来决定size, 因为我们重写了prepareLayout 方法,所以系统的数组并没有创建,这样就无法返回正确的size了,所以我们需要自己在创建布局属性的时候记录一下当前的size,因为我这里高度是固定的,所以只记录宽度就可以了
if (i == count - 1) {
self.width = left + size.width;
}
- (CGSize)collectionViewContentSize
{
return CGSizeMake(self.width, self.collectionView.bounds.size.height);
}
三 、 自定义layout 中系统不支持直接获取某个item的size ,需要我们自定义协议并设置代理获取
@protocol TPShareCollectionViewFlowLayoutProtocol <NSObject>
- (CGSize)collection:(UICollectionView *)collectionView sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
CGSize size = [self.delegate collection:self.collectionView sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
CGRect frame = CGRectMake(left, 0, size.width, size.height);
四、 总结起来就是 : 每个item frame 的origin 需要layout 自己记录维护,size 需要通过代理获取
完整代码
.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol TPShareCollectionViewFlowLayoutProtocol <NSObject>
- (CGSize)collection:(UICollectionView *)collectionView sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface TPShareCollectionViewFlowLayout : UICollectionViewFlowLayout
@property (nonatomic, weak) id <TPShareCollectionViewFlowLayoutProtocol> delegate;
@end
NS_ASSUME_NONNULL_END
.m
#import "TPShareCollectionViewFlowLayout.h"
@interface TPShareCollectionViewFlowLayout ()
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, strong) NSMutableArray *layoutAttributesArray;
@end
@implementation TPShareCollectionViewFlowLayout
- (void)prepareLayout
{
[super prepareLayout];
[self configLayout];
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return self.layoutAttributesArray;
}
- (void)configLayout
{
[self.layoutAttributesArray removeAllObjects];
NSInteger count = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
CGFloat left = 0;
for (NSInteger i = 0; i < count ;i ++) {
// 创建布局属性
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
CGSize size = [self.delegate collection:self.collectionView sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
CGRect frame = CGRectMake(left, 0, size.width, size.height);
layoutAttributes.frame = frame;
[self.layoutAttributesArray addObject:layoutAttributes];
if (i == count - 1) {
self.width = left + size.width;
} else {
left += (size.width + self.minimumLineSpacing);
}
}
}
- (CGSize)collectionViewContentSize
{
return CGSizeMake(self.width, self.collectionView.bounds.size.height);
}
- (NSMutableArray *)layoutAttributesArray
{
if (!_layoutAttributesArray) {
_layoutAttributesArray = [NSMutableArray array];
}
return _layoutAttributesArray;
}
@end