UICollectionView自定义布局(二)
2017-11-06 本文已影响85人
_誌念
这是UICollectionView
自定义布局的第二篇,实现类似UltravisualApp的视差效果,同样这篇文章的教程来自Ray家的Swift Expanding Cells in iOS Collection Views这篇文章。
自定义布局
将该动画分解,首先实现如下图所示的效果。
初始效果.gif随着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下载。