UICollectionView 实现专辑封面视差滚动

本文翻译自:Parallax scrolling album covers with UICollectionView

视差效果现在风靡一时,iOS7上更是使用了很多。在新的音乐App中,在iTunes Radio中,都有一种我非常喜欢的特别的视差效果。滚动的专辑封面栈。实现这个效果似乎是一个非常有趣的挑战,今天我将向你展示如何创建这个效果。当然,我们使用的是UICollectionView。下面是最终的效果。

在我开始写代码之前,我想先解释一下我的方法。我们将创建一个UICollectionViewCell,然后放置一个UIImageView在它的中间位置。这张照片是固定和等量的填充。然后我们将创建几个图像视图,每一个视图都稍微从原来的位置插入,并放在固定的图片视图后面。这些图片将根据当前单元格的滚动位置来流动,就是向左和向右移动,占据空余的空间。

这里你可以看到我描述的单元格的轮廓。蓝色的轮廓是我们固定的图片,绿色的轮廓是我们单元格的界限,而我们流动的图片用灰色轮廓标识。

接下来让我们决定这个流动图片怎么移动。我们需要知道每一个单元格相对于集合视图界限的位置。让我们创建一个通用的压缩系数来表示这个信息:

-1 代表单元格滚动到视图的右边。

0 代表单元格完美地位于视图的中间。

1 代表单元格滚动到视图的左边。

这称为标准化(normalization),我们可以做一个简单的线性方程:

- (CGFloat)parallaxPositionForCell:(UICollectionViewCell *)cell {

    CGRect frame = [cell frame];
    CGPoint point = [[cell superview] convertPoint:frame.origin toView:collectionView];

    const CGFloat minX = CGRectGetMinX([collectionView bounds]) - frame.size.width;
    const CGFloat maxX = CGRectGetMaxX([collectionView bounds]);

    const CGFloat minPos = -1.0f;
    const CGFloat maxPos = 1.0f;

    return (maxPos - minPos) / (maxX - minX) * (point.x - minX) + minPos;
}

现在我们需要一个方法来在collectionView 滚动时发送这些信息给每一个单元格:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    for (id cell in [collectionView visibleCells]) {
        CGFloat position = [self parallaxPositionForCell:cell];
        [cell setParallaxPosition:position]; // We will implement this next.
    }
}

接下来我们需要在我们自定义的单元格类中实现setParallaxPosition:方法。这个方法将负责根据位置来移动我们的流动图片。因为我们使用通用的压缩系数,我们应该为单元格定义这些值代表什么。

-1 代表单元格滚动到视图的右边

UICollectionView 实现专辑封面视差滚动

0 代表单元格完美地位于视图的中间

UICollectionView 实现专辑封面视差滚动

1 代表单元格滚动到视图的左边

UICollectionView 实现专辑封面视差滚动

现在在我们自定义的单元格类中添加如下实现:

- (void)setParallaxPosition:(CGFloat)position {

    CGRect bounds = [self bounds];

    // We only use the height dimension for our image view. So the padding
    // on either side will be the difference in width divided by 2.
    const CGFloat padding = (bounds.size.width - bounds.size.height) / 2.0;

    const CGFloat minOffsetX  = -padding;
    const CGFloat maxOffsetX  = padding;

    const CGFloat minPosition = 1.0;
    const CGFloat maxPosition = -1.0;

    // Compute the total offset using a linear equation
    CGFloat offsetX = (maxOffsetX - minOffsetX) / (maxPosition - minPosition) * (position - minPosition) + minOffsetX;

    // Divide the total offset among the images that will be moved
    offsetX /= ([imageViews count] - 1);

    // Apply the offsetX to each image relative to the first one
    CGRect fixedRect = [[imageViews objectAtIndex:0] frame];
    for (NSInteger i = 1; i < [imageViews count]; i++) {

        UIImageView *imageView = [imageViews objectAtIndex:i];
        CGRect imageRect = [imageView frame];
        CGFloat imageWidth = imageRect.size.width;
        imageRect.origin.x = CGRectGetMidX(fixedRect) - 0.5 * imageWidth + (offsetX * i);
        [imageView setFrame:imageRect];
    }
}

我们在这里所做的跟我们之前的非常相似。我们把位置转为-1 到 1范围内,然后转换为一个offsetX值。然后我们遍历这个流动图片视图,应用这个偏移量到每个相对于固定图片的frame上。

你可以在 Github 上查看完整的代码

你可能感兴趣的:(Collection)