这是UICollectionView
自定义布局的第三篇,实现另一种视差效果,要实现的效果如下图所示。你也可以查看这篇文章。
1. 对Cell进行Transform变换
首先对ItermCell
进行Transform
变换。重写layoutAttributesForElementsInRect
方法,遍历所有的布局属性,将cell按逆时针旋转14°,代码如下:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
/*获取当前可视区域内的布局属性*/
NSArray *original = [super layoutAttributesForElementsInRect:rect];
NSArray *layoutArray = [[NSArray alloc]initWithArray:original copyItems:YES];
CGFloat angle = M_PI *(-14/180.0);
for (UICollectionViewLayoutAttributes *attributes in layoutArray) {
/*cell按逆时针旋转14°*/
attributes.transform = CGAffineTransformMakeRotation(angle);
}
return layoutArray;
}
注意布局属性数组layoutArray
一定要进行copy
,否则会报下面这个警告:
This is likely occurring because the flow layout subclass ParallaxEffectlayout is modifying attributes returned by UICollectionViewFlowLayout without copying them
运行结果如下:
sss.png
从运行结果我们发现几个问题:
- 第一个
itermCell
和最后一个itermCell
超出了屏幕范围。 -
itermCell
左右两侧有空白区域,不美观。 - 背景图片也跟随Cell旋转了14°,我们希望背景图不进行旋转。
2. 解决Transform后出现的问题
1. 第一个问题
可以通过设置CollectionView
的contentInset
来调整。
_myCollection.contentInset = UIEdgeInsetsMake(50, 0, 50, 0);
2. 第二个问题
可以在cell上添加一个宽度大于cell宽度的ContainerView
,作为图片的父视图。
[self.contentView addSubview:self.containerview];
[self.containerview mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(self.contentView);
make.left.equalTo(self.contentView).offset(-40);
make.right.equalTo(self.contentView).offset(40);
}];
[self.containerview addSubview:self.backImageView];
[self.backImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.bottom.equalTo(self.containerview);
}];
调整后的运行结果如下:
调整后的运行结果.png
3. 第三个问题不让图片进行旋转
只需要将图片反方向旋转14°,就可以抵消ItermCell的旋转影响。图片旋转后会出现锯齿,可以设置layer.allowsEdgeAntialiasing = YES;
来改变,参考图片变形的抗锯齿处理方法。
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{
[super applyLayoutAttributes:layoutAttributes];
/*让imageView不跟随cell进行旋转*/
CGFloat angle = M_PI *(14 / 180.0);
self.backImageView.transform = CGAffineTransformMakeRotation(angle);
}
调整图片方向后的效果.png
3. 添加视差效果
我们来添加一个随列表滚动的一个视差效果,原理也很简单,就是 cell 向一个方向滑动时,相应的imageView
向相反的方向去运动。
- (void)parallaxOffsetForCollectionBounds:(CGRect)collectionBounds{
//collectionView和cell的中心点
CGRect bounds = collectionBounds;
CGPoint boundsCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint cellCenter = self.center;
//找出每个cell相对于 collectionView 中心点的偏移量
CGPoint offsetFromCenter = CGPointMake(boundsCenter.x - cellCenter.x, boundsCenter.y - cellCenter.y);
//cell 的最大偏移量
CGSize cellSize = self.bounds.size;
CGFloat maxVerticalOffsetWhereCellIsStillVisible = (bounds.size.height / 2) + (cellSize.height / 2);
CGFloat scaleFactor = 40.0 / maxVerticalOffsetWhereCellIsStillVisible;
CGPoint parallaxOffset = CGPointMake(0.0, offsetFromCenter.y * scaleFactor);
[self.backImageView mas_updateConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.containerview.mas_centerY).offset(parallaxOffset.y);
}];
}
offsetFromCenter.y
表示偏离中心点的距离。这就意味 collectionView 滚动一段距离,离中心点越远的 cell 对应的 parallaxOffset
的值就越大,离中心点越近的 cell 对应的 parallaxOffset
值就越小,这也符合我们的视觉习惯,目光平视中心点,滚动起来两边的视觉差要大一些。
计算偏移量后更新Y轴的约束:
[self.backImageView mas_updateConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.containerview.mas_centerY).offset(parallaxOffset.y);
}];
在CollectionView中调用scrollViewDidScroll
方法,监听滑动的时候实时更新约束。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSArray *cellArray = [self.myCollection visibleCells];
[cellArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NormalCollectionViewCell *cell = obj;
[cell parallaxOffsetForCollectionBounds:self.myCollection.bounds];
}];
}
到此,视差效果已全部完成,文中demo可以到GitHub下载。
网友评论