UICollectionView自定義佈局(二)

_執念發表於2019-03-04

這是UICollectionView自定義佈局的第二篇,實現類似UltravisualApp的視差效果,同樣這篇文章的教程來自Ray家的Swift Expanding Cells in iOS Collection Views這篇文章。

最終效果.gif

自定義佈局

將該動畫分解,首先實現如下圖所示的效果。

初始效果.gif

隨著CollectionView的滑動,itermCellframe的變化如下圖所示:

UICollectionView自定義佈局(二)

itermCell分為三種型別:

  1. FeaturedCell : 突出顯示的cell,高度為featuredHeight
  2. StandardCell : 標準狀態下的cell,高度為standardHeight
  3. 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;
}
複製程式碼
  1. attribute.zIndex的值隨著iterm的增加逐漸增大,形成上圖所示的覆蓋效果。
  2. ChangedCell的高度隨著偏移距離,由standardHeight變化為featuredHeight
  3. 從視覺上看由StandardCell變為FeaturedCell只移動了standardHeight的距離,但是實際上contentOffSet.y移動的距離大於這個值,實際上移動了self.dragOffset才能完成這個變換。
  4. 詳細的程式碼如下所示。
- (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);
}
複製程式碼

新增圖片背景和詳情內容

圖片和文字的建立程式碼比較簡單就不列出了,需要注意的是:

  1. UIImageViewcontentMode設定為UIViewContentModeScaleAspectFill。並且設定layer.masksToBounds = YES
  2. 一定要使用自動佈局否則會顯示不正常。

重寫applyLayoutAttributes

  1. 根據偏移量改變黑色CoverView的背景色,突出顯示FeaturedCell
  2. 根據偏移量對titleLabel進行仿射變換。
  3. 根據偏移量對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下載

相關文章