Cover Flow布局
Cover Flow布局的实现,可参见教程:
本文内容也是来源于此
实现过程
Cover Flow布局也是一种Flow布局,所以布局的类可直接继承自UICollectionViewFlowLayout
基本设置
1.设置scrollDirection
为UICollectionViewScrollDirectionHorizontal
,表示水平滚动
需要注意的是,垂直滚动和水平滚动,其行间距和item间距的不同:
2.设置minimumInteritemSpacing
为一个合理的值,这样item就可以在一行显示,而不是多行显示
3.设置minimumLineSpacing
为负值,这样item就叠加显示
自定义布局
自定义布局中最主要的是还是根据item的中心点距collection view中心的距离,进行3D变换
1.当某个item
的中心距离visibleRect
的中心点小于ACTIVE_DISTANCE
(一个定义的常量)时,就需要进行3D变换,这是中间item的效果
a.只进行translate
的效果
b.再添加上rotate
后的效果
c.再添加上scale
后的效果
2.对于距离visibleRect
的中心点太大的item,主要添加2个变换
a.添加translate
后的效果
b.再添加rotate
后的效果
其主要代码如下:
- (void)setCellAttributes:(UICollectionViewLayoutAttributes *)attributes forVisibleRect:(CGRect)visibleRect
{
// 对给定的布局attributes应用cover flow效果
// 跳过supplementary views.
if (attributes.representedElementKind) return;
// 计算距离可见区域中心的距离
CGFloat distanceFromVisibleRectToItem = CGRectGetMidX(visibleRect) - attributes.center.x;
CGFloat normalizedDistance = distanceFromVisibleRectToItem / ACTIVE_DISTANCE;
BOOL isLeft = distanceFromVisibleRectToItem > 0;
CATransform3D transform = CATransform3DIdentity;
if (fabsf(distanceFromVisibleRectToItem) < ACTIVE_DISTANCE)
{
transform = CATransform3DTranslate(CATransform3DIdentity, (isLeft? - FLOW_OFFSET : FLOW_OFFSET)*ABS(distanceFromVisibleRectToItem/TRANSLATE_DISTANCE), 0, (1 - fabsf(normalizedDistance)) * 40000 + (isLeft? 200 : 0));
// 设置透视
transform.m34 = -1/(4.6777 * self.itemSize.width);
CGFloat zoom = 1 + ZOOM_FACTOR*(1 - ABS(normalizedDistance));
transform = CATransform3DRotate(transform, (isLeft? 1 : -1) * fabsf(normalizedDistance) * 45 * M_PI / 180, 0, 1, 0);
transform = CATransform3DScale(transform, zoom, zoom, 1);
attributes.zIndex = 1;
}
else
{
transform.m34 = -1/(4.6777 * self.itemSize.width);
transform = CATransform3DTranslate(transform, isLeft? -FLOW_OFFSET : FLOW_OFFSET, 0, 0);
transform = CATransform3DRotate(transform, (isLeft? 1 : -1) * 45 * M_PI / 180, 0, 1, 0);
attributes.zIndex = 0;
}
attributes.transform3D = transform;
}
还有一点就是,在滚动停止的时候,让某个item居中。
实现- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
方法即可
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
// 返回想要collection view停止的位置
// 首先计算想要collection view停止的位置
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);
// 使用center找到可视区域
CGRect proposedRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
// 获取可视区域中cell的attribute
NSArray* array = [self layoutAttributesForElementsInRect:proposedRect];
// 遍历找到距离中心最近的cell
for (UICollectionViewLayoutAttributes* layoutAttributes in array)
{
// 跳过supplementary views
if (layoutAttributes.representedElementCategory != UICollectionElementCategoryCell)
continue;
// 找到最小值
CGFloat itemHorizontalCenter = layoutAttributes.center.x;
if (fabsf(itemHorizontalCenter - horizontalCenter) < fabsf(offsetAdjustment))
{
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
一些问题
1.item显示的时候锯齿化严重
在实例教程中,是通过设置光栅化来解决的,需要自定义布局属性类
2.collection view中的第一个item和最后一个item不能滚动到中间
可设置collection view的section inset来解决
3.响应点击事件
上面提到的教程中,也给出了方法