天天看点

对collectionView切换不同layout,实现不同的layout布局

转自: https://www.jianshu.com/p/539ef116c525

由于项目需求,需要在同一个collectionView上面,实现不同的layout布局。一个是固定item大小,没有动画的layout,这个用UICollectionViewFlowLayout就可以实现;另一种是实现水平布局,中间放大,能够看见两边item边缘的效果。

对collectionView切换不同layout,实现不同的layout布局

基于以上需求,在对第一个布局,我直接设置一下UICollectionViewFlowLayout对象的itemSize就可以完成。但是对于第二种的水平移动中间放大的效果,需要自定义layout。下面我就详细的叙述一下,我在自定义layout时,遇到的坑。

在自定义UICollectionViewFlowLayout的过程中,需要重写三个方法:

1.- (void)prepareLayout;主要用于相关参数的初始化,比如itemSize,minimumLineSpacing,minimumInteritemSpacing,setScrollDirection(设置滚动方向,sectionInset(整个组的四周边距);

- (void)prepareLayout{

lastOffset = self.collectionView.contentOffset.x;

//设置每一个cell的尺寸

float itemWidth = SCR_WIDTH - 134;

//设置每一个cell的尺寸

self.itemSize = CGSizeMake(itemWidth, getCellHeight(itemWidth));

self.minimumLineSpacing = 38;

//滑动方向

[self setScrollDirection:UICollectionViewScrollDirectionHorizontal];

CGFloat inset = (self.collectionView.width - itemWidth) / 2.0;

//把item的左右边切掉,让item处在屏幕中间位置

//  UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)

self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);

}

在这方法中,一定要注意设置sectionInset,因为它决定了你横向移动首位item的位置。我在一开始,当你发现第一个位置,和最后一个位置,不是自己想要的,你就要开始从这个地方入手考虑了。

2.- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;这个方法是获取所有item的layoutAttributes;

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{

//计算可见的矩形框

CGRect visiableRect;

visiableRect.size = self.collectionView.frame.size;

visiableRect.origin = self.collectionView.contentOffset;

//1.取得cell原来的UICollectionViewLayoutAttributes

NSArray *array = [super layoutAttributesForElementsInRect:rect];

//屏幕中间的X

CGFloat screenCenterX = self.collectionView.contentOffset.x + self.collectionView.width/2.0;

//2.遍历所有的布局属性

[array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

if (CGRectIntersectsRect(visiableRect, obj.frame)) {

//每个item的centerX

CGFloat itemCenterX = obj.center.x;

//差距越小,缩放越大

//计算缩放比例

CGFloat scale = 1 + 0.25 * (1 - ABS(itemCenterX - screenCenterX) / (self.collectionView.width/2));

obj.transform3D = CATransform3DMakeScale(scale, scale, 1);

}

}];

return array;

}

在这个方法中,可以获取当前屏幕显示的显示的item,也就是视觉上所看到,从而找到需要做动画的item

3.- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity。这个方法是获取滑动停止的位置,根据我的需求,我需要在停止的时候,找到离屏幕中间最近的item,并将它显示在屏幕中间。代码如下:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{    // 判断是否为第一个    if (proposedContentOffset.xself.collectionViewContentSize.width-SCR_WIDTH*1.5+self.sectionInset.right) {

return CGPointMake(self.collectionViewContentSize.width-SCR_WIDTH, 0);

}

//1.获取collectionView最后停留的范围

CGRect lastRect;

lastRect.origin = proposedContentOffset;

lastRect.size = self.collectionView.frame.size;

//2.取出这个范围类所有布局属性

NSArray *array = [self layoutAttributesForElementsInRect:lastRect];

//3.遍历所有布局的属性

//停止滑动时item应该在的位置

__block CGFloat adjustOffsetX = MAXFLOAT;

//屏幕中间的X

CGFloat screenCenterX = proposedContentOffset.x + self.collectionView.width/2.0;

[array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

if (ABS(obj.center.x - screenCenterX) < ABS(adjustOffsetX)) {

adjustOffsetX = obj.center.x - screenCenterX;

}

}];

return CGPointMake(proposedContentOffset.x + adjustOffsetX, proposedContentOffset.y);

}

以上3个方法,可以基本完成一个水平滑动的中间放大的layout的要求,在对两个layout的切换,只需要设置setCollectionViewLayout:方法就行了。对于我现在的任务来说,我目前无法完成,滑动速度的控制。

作者:EmptyWalker

链接:https://www.jianshu.com/p/539ef116c525

來源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

继续阅读