关键方法
-
1
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
该方法用来返回rect范围内的 cell supplementary 以及 decoration的布局属性layoutAttributes(这里保存着她们的尺寸,位置,indexPath等等),如果你的布局都在一个屏幕内或者没有复杂的计算,这里可以返回全部的属性数组,如果涉及到复杂计算,应该进行判断,返回区域内的属性数组,有时候为了方便直接返回了全部的属性数组,不影响布局但可能会影响性能(如果你的item一屏幕显示不完,那么这个方法会调用多次,当所有的item都加载完毕后,在滑动collectionView时不会调用该方法的)。
-
2
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
该方法不是必须实现的,即便你实现了,我们对collectionView的任何操作,也不会导致系统主动调用该方法。该方法通常用来定制某个IndexPath的item的属性。当然我们也可以重写这个方法,将布局时相关的属性设置放在这里,在
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
或者- (void)prepareLayout
中 需要创建用来返回给系统的属性数组 主动调用这个方法,并添加带可变数组中去返回给系统。当然我们也可以在- (void)prepareLayout
中 通过[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]
获取 每个indexPath的attributes,在- (void)prepareLayout
中设置所有item的属性。看需求以及个人喜欢。 -
3
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
用来刷新layout的,当我们返回yes的时候。如果我们的需求不需要实时的刷新layout,那么最好判断newBounds 和 我们的collectionView的bounds是否相同,如果不同就返回yes;(例如苹果官方的lineLayout,因为每次滑动都要放大item,所以这了就直接返回yes)。
-
4
- (void)prepareLayout;
第一次加载layout、刷新layout、以及
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
这个方法返回yes时,会调用。实现该方法后应该调用[super prepareLayout]
保证初始化正确。该方法用来准备一些布局所需要的信息。该方法和init方法相似,但该方法可能会被调用多次,所以一些不固定的计算(比如该计算和collectionView的尺寸相关),最好放在这里,以保证collectionView发生变化时,自定义CollectionView能做出正确的反应。
举例
需要展示大小不相等的几张图片
说明:图片0比其他几张图片大,从而影响到布局,所以我们自定义一种布局
@interface SixImageLayout : UICollectionViewLayout
两个关键方法
返回所有cell的布局
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
self.cellCount = 6; 共6个cell
NSMutableArray *attributes = [NSMutableArray array];
for (NSInteger i = 0; i < self.cellCount; i ++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
//调用下面的方法返回
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[attributes addObject:attrs];
}
return attributes;
}
计算每个位置的布局
- (UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath {
UICollectionViewLayoutAttributes attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.size = CGSizeMake(kCellSize, kCellSize);
if (indexPath.row == 0) {
attributes.size = CGSizeMake(kCellSize2+1, kCellSize2+1);
attributes.center = CGPointMake((kCellSize2+1)/2, (kCellSize2+1)/2);
}
else if (indexPath.row == 1) {
attributes.center = CGPointMake(kCellSize2+2+kCellSize/2, kCellSize/2);
} else if(indexPath.row == 2) {
attributes.center = CGPointMake(kCellSize2+2+kCellSize/2, kCellSize+kCellSize/2+1);
}else if (indexPath.row == 3) {
attributes.center = CGPointMake(kCellSize/2, kCellSize2+2+kCellSize/2);
} else if(indexPath.row == 4) {
attributes.center = CGPointMake(kCellSize+kCellSize/2+1, kCellSize2+2+kCellSize/2);
} else {
attributes.center = CGPointMake(kCellSize2+2+kCellSize/2, kCellSize*2+2+kCellSize/2);
}
return attributes;}
然后设置布局
SixImageLayout *sixLayout = [[SixImageLayout alloc] init];
self.collectionV = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 110, kScreenWidth, kScreenWidth) collectionViewLayout:sixLayout];