這是UICollectionView
自定義佈局的第二篇,實現相似UltravisualApp的視差效果,一樣這篇文章的教程來自Ray家的Swift Expanding Cells in iOS Collection Views這篇文章。ios
將該動畫分解,首先實現以下圖所示的效果。git
隨着CollectionView的滑動,itermCell
的frame
的變化以下圖所示: github
itermCell
分爲三種類型:
featuredHeight
。standardHeight
。contentOffSet
改變而改變的cell,高度的變化範圍在standardHeight和featuredHeight之間。(FeaturedCell下面的那個cell)- (int)featuredItemIndex{
int index = (int)(self.collectionView.contentOffset.y / self.dragOffset);
return MAX(0, index);
}
複製代碼
self.dragOffset
是拖拽距離(當偏移量大於這個值時,featuredItemIndex的索引會變爲下一個)。由當前FeaturedCell
的索引index能夠得到ChangedCell
的索引爲index+1,進而獲得其餘的索引位置就是StandardCell
。swift
隨着collectionView的滑動,standardCell 變化爲 featuredCell,變化範圍爲[0 ,1]。bash
- (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];
}
複製代碼
當ItermCell
滾動的時候,將其停在固定的點。使其滾動中止時,屏幕顯示的第一個cell永遠是FeaturedCell
。app
- (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
。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下載。字體