自定义UICollectionViewFlowLayout

闲着没事,无意间看到掌上英雄联盟查看皮肤中CollectionView的效果,觉得很不错,趁着机会也学习下UICollectionViewFlowLayout的自定义,先看效果:

查找资料,发现苹果提供了2个关于Layout的demo,

Apple demo下载

LineLayoutDemo下载

这个基本上就是复制LineLayout的学习,在原来的基础上优化了一点。

首先创建一个继承与UICollectionViewFlowLayout的类,
我的.h

1
@interface GLCollectionViewFlowLayou : UICollectionViewFlowLayout

实现文件中主要有四个方法:

1
2
3
4
5
//可以在此方法中初始化你的layout
- (void)prepareLayout {
//必须调用super
[super prepareLayout];
}
1
2
3
//在这里改变布局 滑动时会时时调用此方法
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
}
1
2
3
//替换最终滑动的contentOffset, proposedContentOffset是预期滑动停止的位置
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
}
1
2
3
4
// 当collectionView bounds改变时,是否重新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}

苹果的LineLayout在滑到第一个和最后一个的时候,有点问题,不能更新到正确的位置。

在设置layout的时候设置UICollectionView的_collectionView.contentInset,设置如下:

1
2
CGFloat left = CGRectGetWidth(self.collectionView.frame) / 2 - flowLayout.itemSize.width / 2;
_collectionView.contentInset = UIEdgeInsetsMake(0, left, 0, left);

customLayout.m中,在targetContentOffsetForProposedContentOffset方法中处理第一个和最后一个的返回的contentOffset:

1
2
3
4
5
6
7
8
9
//替换希望的contentOffset
CGPoint actualPoint = CGPointMake(proposedContentOffset.x + minDistance, proposedContentOffset.y);

//处理第一个 和 最后一个的contentOffset
CGFloat minContentOffsetX = -floor(self.collectionView.contentInset.left);
CGFloat maxContentOffsetX = floor(self.collectionView.contentSize.width - CGRectGetWidth(self.collectionView.frame) + self.collectionView.contentInset.right);
actualPoint = actualPoint.x < minContentOffsetX ? CGPointMake(minContentOffsetX, actualPoint.y) : actualPoint;
actualPoint = actualPoint.x > maxContentOffsetX ? CGPointMake(maxContentOffsetX, actualPoint.y) : actualPoint;
return actualPoint;

这样就OK了,滑倒第一个和最后一个也可以正常的显示和滚动了。

Demo地址:

点我哦