UICollectionView自定义布局(二)
这是UICollectionView自定义布局的第二篇,实现类似UltravisualApp的视差效果,同样这篇文章的教程来自Ray家的Swift Expanding Cells in iOS Collection Views这篇文章。

自定义布局
将该动画分解,首先实现如下图所示的效果。

随着CollectionView的滑动,itermCell的frame的变化如下图所示:

itermCell分为三种类型:- FeaturedCell : 突出显示的cell,高度为
featuredHeight。 - StandardCell : 标准状态下的cell,高度为
standardHeight。 - ChangedCell : 高度随着
contentOffSet改变而改变的cell,高度的变化范围在standardHeight和featuredHeight之间。(FeaturedCell下面的那个cell) 
1.获取FeaturedCell的索引
- (int)featuredItemIndex{
   int index = (int)(self.collectionView.contentOffset.y / self.dragOffset);
   return MAX(0, index);
}self.dragOffset是拖拽距离(当偏移量大于这个值时,featuredItemIndex的索引会变为下一个)。由当前FeaturedCell的索引index可以获得ChangedCell的索引为index+1,进而得到其他的索引位置就是StandardCell。
2.重写prepareLayout方法
随着collectionView的滑动,standardCell 变化为 featuredCell,变化范围为[0 ,1]。
- (CGFloat)nextItemPercentageOffset{
    CGFloat percent = (self.collectionView.contentOffset.y / self.dragOffset) - [self featuredItemIndex];
    return percent;
}attribute.zIndex的值随着iterm的增加逐渐增大,形成上图所示的覆盖效果。ChangedCell的高度随着偏移距离,由standardHeight变化为featuredHeight。- 从视觉上看由
StandardCell变为FeaturedCell只移动了standardHeight的距离,但是实际上contentOffSet.y移动的距离大于这个值,实际上移动了self.dragOffset才能完成这个变换。 - 详细的代码如下所示。
 
- (void)prepareLayout{
    [super prepareLayout];
    [self.attributesArray removeAllObjects];
    
    CGRect frame = CGRectZero;
    CGFloat y = 0;
    
    NSInteger numberOfIterm = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < numberOfIterm; i++) {
        NSIndexPath *path = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
        /*下一个cell都在之前的cell之上*/
        attribute.zIndex = path.item;
        /*初始化时设置cell的高度都为标准高度*/
        CGFloat height = standardHeight;
        
        if (path.item == [self featuredItemIndex]) {
            /*featured Cell*/
            CGFloat yOffSet = standardHeight * [self nextItemPercentageOffset];
            y = self.collectionView.contentOffset.y - yOffSet;
            height = featuredHeight;
            
        }else if (path.item == [self featuredItemIndex] + 1 && path.item != numberOfIterm){
            /*在featuredCell之下,随着用户滚动逐渐变大*/
            CGFloat maxY = y + standardHeight;
            height = standardHeight + MAX((featuredHeight - standardHeight) * [self nextItemPercentageOffset], 0);
            y = maxY - height;
        }
        frame = CGRectMake(0, y, CGRectGetWidth(self.collectionView.bounds), height);
        attribute.frame = frame;
        [self.attributesArray addObject:attribute];
        
        /*获取下一个cell的初始的Y值*/
        y = CGRectGetMaxY(frame);
    }
    
    //重新刷新collectionView,不然数据会错乱
    [self.collectionView reloadData];
}3.改变滚动停止时的位置
当itermCell滚动的时候,将其停在固定的点。使其滚动停止时,屏幕显示的第一个cell永远是FeaturedCell。
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
    NSInteger currentFeaturedIndex = round(proposedContentOffset.y / self.dragOffset);
    CGFloat yOffSet = currentFeaturedIndex * self.dragOffset;
    return CGPointMake(0, yOffSet);
}添加图片背景和详情内容
图片和文本的创建代码比较简单就不列出了,需要注意的是:
UIImageView的contentMode设置为UIViewContentModeScaleAspectFill。并且设置layer.masksToBounds = YES。- 一定要使用自动布局否则会显示不正常。
 
重写applyLayoutAttributes
- 根据偏移量改变黑色
CoverView的背景色,突出显示FeaturedCell。 - 根据偏移量对
titleLabel进行仿射变换。 - 根据偏移量对
detailLabel进行透明度变化,只有当前cell是FeaturedCell的时候才显示。 
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{
    [super applyLayoutAttributes:layoutAttributes];
    CGFloat standardHeight = 100.0;
    CGFloat featuredHeight = 280.0;
    /*根据移动距离改变CoverView透明度*/
    CGFloat factor = 1 - (featuredHeight - CGRectGetHeight(layoutAttributes.frame))/(featuredHeight - standardHeight);
    CGFloat minAlpha = 0.2;
    CGFloat maxAlpha = 0.75;
    CGFloat currentAlpha = maxAlpha - (maxAlpha - minAlpha) * factor;
    self.coverView.alpha = currentAlpha;
    
    /*改变字体大小*/
    CGFloat titleScale = MAX(0.5, factor);
    self.titleLabel.transform = CGAffineTransformMakeScale(titleScale, titleScale);
    
    /*设置detailLabel的透明度*/
    self.timeAndRoomLabel.alpha = factor;
    self.speakerLabel.alpha = factor;
}至此,自定义布局就全部完成了,Demo链接可以到GitHub下载。
相关推荐
  lijiexiaoge    2019-06-29  
   binglechen    2016-06-07  
   fanxiaoxuan    2014-04-08  
   稀土    2018-01-31  
   郎启旭的写字本    2017-12-28  
   元宝酱拯救地球    2017-11-29  
   软件设计    2017-03-25  
   手机开发    2017-03-10