美文网首页OC学习ios实用开发技巧
UICollectionView自定义布局(三)

UICollectionView自定义布局(三)

作者: _誌念 | 来源:发表于2017-11-07 23:28 被阅读33次

    这是UICollectionView自定义布局的第三篇,实现另一种视差效果,要实现的效果如下图所示。你也可以查看这篇文章

    最终效果.gif

    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

    从运行结果我们发现几个问题:

    1. 第一个itermCell和最后一个itermCell超出了屏幕范围。
    2. itermCell左右两侧有空白区域,不美观。
    3. 背景图片也跟随Cell旋转了14°,我们希望背景图不进行旋转。

    2. 解决Transform后出现的问题

    1. 第一个问题

    可以通过设置CollectionViewcontentInset来调整。

    _myCollection.contentInset = UIEdgeInsetsMake(50, 0, 50, 0);
    
    2. 第二个问题

    可以在cell上添加一个宽度大于cell宽度的ContainerView,作为图片的父视图。

    原理图.png
        [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下载

    相关文章

      网友评论

        本文标题:UICollectionView自定义布局(三)

        本文链接:https://www.haomeiwen.com/subject/ypijmxtx.html