关于UICollectView自定义Layout布局,首先需要定义类继承UICollectionViewFlowLayout,需要重写的方法:
//collectview第一次布局的时候调用
//collectView刷新(reload)的时候会再次调用
//子类必须要调用[super prepareLayout]
- (void)prepareLayout;
//根据指定rect,返回该rect区域内cell的相关信息,UICollectionViewLayoutAttributes -> cell的相关信息(frame、center、size、transform3D、bounds、transform等相关信息)
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
//在滚动的时候是否允许刷新布局,默认为No
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
//当手指抬起的时候调用,确定最终偏移量
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;
//子类必须要重写此方法,计算内容范围,返回collectView的内容区域
- (CGSize)collectionViewContentSize;
实现如下图的一个自定义layout布局:
自定义Layout:
//不需要处理
- (void)prepareLayout
{
[super prepareLayout];
}
//不需要处理,直接调用super返回内容范围
- (CGSize)collectionViewContentSize
{
return [super collectionViewContentSize];
}
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
//获取当前显示区域self.collectionView.bounds内的cell的布局
NSArray *attrs = [super layoutAttributesForElementsInRect:self.collectionView.bounds];
for (UICollectionViewLayoutAttributes *attr in attrs) {
//对应其他特效可在此处处理
CGFloat space = fabs((attr.center.x - self.collectionView.contentOffset.x) - self.collectionView.bounds.size.width / 2);
//计算缩放比例
CGFloat scale = 1 - space / (self.collectionView.bounds.size.width * 0.5) * 0.3;
attr.transform = CGAffineTransformMakeScale(scale, scale);
}
return attrs;
}
//在滚动的时候允许刷新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
//确定最终偏移量,定位cell显示居中
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
//1. 获取UICollectionView停止滚动时的可视范围
CGRect contentFrame;
contentFrame.size = self.collectionView.frame.size;
contentFrame.origin = proposedContentOffset;
//获取可视范围内的cell
NSArray *array = [super layoutAttributesForElementsInRect:contentFrame];
//2. 计算在可视范围的距离中心线最近的Item
CGFloat minCenterX = MAXFLOAT;
//中心线
CGFloat collectionViewCenterX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
for (UICollectionViewLayoutAttributes *attrs in array) {
CGFloat space = attrs.center.x - collectionViewCenterX;
if(fabs(space) < fabs(minCenterX)){
minCenterX = attrs.center.x - collectionViewCenterX;
}
}
//3. 更新ContentOffset,则正好将Item居中显示
return CGPointMake(proposedContentOffset.x + minCenterX, proposedContentOffset.y);
}