实现效果
实现思路
从效果图可以看到变化是,越是往中间滚动的item显示最大,越显眼。而越是往前面,或者越是后面的,反而显示越小,这样就形成了视觉差。
实现的思路就是通过重写在可见范围内的所有item的方法:
1 2 3 |
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect |
通过这个API可以获取到原始属性,然后利用公式来计算缩放。
难点
如何计算缩放系数。这里实现的效果的公式如下:
1 2 3 |
CGFloat zoom = 1 + 0.15 * (1 - fabs(widthForScale)); |
其中,widthForScale是最难把握的系数。
如何计算widthForScale呢?方法是利用item的中心y与当前collectionview的contentOffset.y来计算:
1 2 3 4 |
CGFloat distance = obj.center.y - fabs(offset) - self.itemSize.width; CGFloat widthForScale = distance / self.itemSize.height; |
其中,offset这么计算出来的:
1 2 3 4 5 6 7 |
CGRect visibleRect = CGRectZero; visibleRect.origin = self.collectionView.contentOffset; visibleRect.size = self.collectionView.frame.size; CGFloat offset = CGRectGetMinY(visibleRect); |
最大的难点是distance的计算,如何拿捏呢?这需要慢慢分析。
假设有三个item显示,那么第一个item的缩放要比第二个item(主显示的item)要小,然后第二个item的缩放要比第三个要大。
第二个item(主)比第一个多了一个item,而第三个又比第二个多了一个item,所以再减去一个item的宽就可以了。
核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *superAttributes = [super layoutAttributesForElementsInRect:rect]; NSArray *attributes = [[NSArray alloc] initWithArray:superAttributes copyItems:YES]; CGRect visibleRect = CGRectZero; visibleRect.origin = self.collectionView.contentOffset; visibleRect.size = self.collectionView.frame.size; CGFloat offset = CGRectGetMinY(visibleRect); [attributes enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL * _Nonnull stop) { CGFloat distance = obj.center.y - fabs(offset) - self.itemSize.width; CGFloat widthForScale = distance / self.itemSize.height; CGFloat zoom = 1 + 0.15 * (1 - fabs(widthForScale)); obj.transform3D = CATransform3DMakeScale(zoom, 1.0, 1.0); }]; return attributes; } |
系数0.15自由调整。