美文网首页实用工具iOS进阶指南iOS开发
对collectionView切换不同layout的总结

对collectionView切换不同layout的总结

作者: EmptyWalker | 来源:发表于2016-01-08 16:30 被阅读8894次

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

    基于以上需求,在对第一个布局,我直接设置一下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;

    /** 

    * 所有item的布局属性 

    */

    - (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,并将它显示在屏幕中间。代码如下:

    /** 

    *用来设置collectionView停止滚动的那一刻的位置 proposedContentOffset: collectionView停止滚动的位置 velocity : 滚动速度 

    */

    - (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:方法就行了。对于我现在的任务来说,我目前无法完成,滑动速度的控制。

    相关文章

      网友评论

      • 0x00chen:比如layout1有1个cell,layout2有10个cell,当切换到layout2 滑到第二个cell,这时候点击切换layout就会崩溃,这个问题怎么处理。楼主能提供下思路吗
        super耗子王:在- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;这个代理方法中判断一下状态,根据不同状态返回不同的值就行了
      • 0x00chen:对于不同的layout 有不同个数的cell,当我切换时候回报错,这个怎么解决啊
      • SoaringHeart:uicollectionview 不同section 不同layout 能实现吗?
        Lol刀妹:@SoaringHeart 赶紧想,还有类似淘宝首页那种只是最后一个模块是瀑布流的collectionView
        SoaringHeart:@陌上花开下雨天 暂时没想到,想到了会发文章的
        沐时:老哥,你解决这个问题了吗?
      • LD_左岸:每次滑动的距离固定吗 就是左右滑的时候 能不能搞个分页效果出来
      • 这个熊孩子不太冷:你好,我重写了layout方法,- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath这个方法不走了
        这个熊孩子不太冷:@Resoluted 我觉得你这个需求可以用tableView来实现
        Resoluted:请问您一下UICollectionView 如何实现在拥有多个分组的时候,整体是垂直滚动,每组是水平滚动
        EmptyWalker:@这个熊孩子不太冷 https://github.com/emptywalker/XYHCollectionViewLayout 代码在这边 你可以参考一下

      本文标题:对collectionView切换不同layout的总结

      本文链接:https://www.haomeiwen.com/subject/skofkttx.html