這是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下載。