这是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下载。字体