效果
标题是collectionView,但其实这篇文章说的是自定义FlowLayout的事😄
大家都知道collectionView关于元素的事情,更多是于布局决定的。
比如滚动方向,元素间距,元素尺寸,布局的组间距,布局的行间距(这些都决定了元素的位置)。
如果要实现画廊效果只需要关心三个方法
/**
* 当bounds发生改变(滚动就是改变了bounds,改变了可视区)的时候是否允许刷新布局。
*
* @param newBounds
*
* @return 默认返会NO
*/
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
/**
* 返回区域内的cell,这个方法做根据cell距离中心点 来缩放
*
* @param rect 区域
*
* @return 区域内的cell
*/
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
//bounds的改变就是可视范围的改变, 那么我们去拿可视范围内的cell
NSArray *attrs = [super layoutAttributesForElementsInRect:self.collectionView.bounds];
//计算每一个cell与中心点的距离, 并通过cell与中心点距离的改变来缩放cell
for (UICollectionViewLayoutAttributes *attr in attrs) {
CGFloat delta = fabs((attr.center.x - self.collectionView.contentOffset.x) - self.collectionView.frame.size.width * 0.5);
CGFloat scale = 1 - (delta / self.collectionView.bounds.size.width * 0.5) * 0.55;
attr.transform = CGAffineTransformMakeScale(scale, scale);
}
return attrs;
}
/**
* 计算最终的偏移量, 在这个方法里做判断最小距离自动滚动到中间效果
*
* @param proposedContentOffset 预计偏移
* @param velocity 速率
*
* @return 最终偏移
*/
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
CGFloat collectionW = self.collectionView.bounds.size.width;
CGFloat collectionH = self.collectionView.bounds.size.height;
// 1确定最终的滚动位置
CGPoint targetP = [super targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:velocity];
// 2利用滚动位置去拿当前显示的所有元素
CGRect rect = CGRectMake(targetP.x, 0, collectionW, collectionH);
NSArray<UICollectionViewLayoutAttributes *> *attrs = [super layoutAttributesForElementsInRect:rect];
// 3获取所有滚动元素距离中心点的最小值
CGFloat minDelta = MAXFLOAT;
for (UICollectionViewLayoutAttributes *attr in attrs) {
CGFloat delta = attr.center.x - targetP.x - collectionW * 0.5;
if (fabs(delta) < fabs(minDelta))
minDelta = delta;
}
// 4用最终的偏移量加最小值 形成自动滑动的效果
targetP.x += minDelta;
if (targetP.x < 0)
targetP.x = 0;
return targetP;
}
bounds
bounds的origin是可以改变的,取决于有没有可视范围外的可滚动区域。
scrollView的滚动是通过可视范围的改变(bounds)来实现滚动的。
想要实现这个效果的朋友直接自定义FlowLayout,重写以上三个方法即可。