美文网首页iOS开发技巧
有间距的无限轮播图

有间距的无限轮播图

作者: nickNameDC | 来源:发表于2018-03-06 09:12 被阅读258次

    最近需求中需要用到中间有间隔的无限轮播图,中间还要放大。所以仿照SDCycleScrollView的原理自己写了一个无限轮播图。

    QQ20180305-203421.gif

    SDCycleScrollView实现思想

    利用UICollectionView的Cell重用机制,完美解决了创建多个UIImageView的性能问题。首先创建一个UICollectionView,然后创建根据图片的个数创建item,如果有4张图片则创建4*100个item,也就是100组图片,然后定位初始位置在中间那组的一个。当滑动到最后一个item的时候把offset再移动到中间。
    当然这种方法只是实现了自动轮播的无限,如果是手动滑动还是可以滑到尽头的,不过相信很少人会这么无聊滑动到最后。
    下面看一下核心逻辑代码。

    初始化collectionView的时候如果是无限循环则把item滑动到中间
        if(self.collectionView.contentOffset.x == 0 && _totalItems > 0)
    {
            NSInteger targeIndex = 0;
            if(self.infiniteLoop)
            {//无线循环
                // 如果是无限循环,应该默认把 collection 的 item 滑动到 中间位置。
                // 注意:此处 totalItems 的数值,其实是图片数组数量的 100 倍。
                // 乘以 0.5 ,正好是取得中间位置的 item 。图片也恰好是图片数组里面的第 0 个。
                targeIndex = _totalItems * 0.5;
            }else
            {
                targeIndex = 0;
            }
            //设置图片默认位置
            [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targeIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
        }
    }
    
    计算当前index的逻辑
        if (_mainView.sd_width == 0 || _mainView.sd_height == 0) {
            return 0;
        }
        
        int index = 0;
        if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
            index = (_mainView.contentOffset.x + _flowLayout.itemSize.width * 0.5) / _flowLayout.itemSize.width;
        } else {
            index = (_mainView.contentOffset.y + _flowLayout.itemSize.height * 0.5) / _flowLayout.itemSize.height;
        }
        
        return MAX(0, index);
    

    轮播图中间添加间距

    怎么让轮播图中间有间距呢?因为每个图片是一个cell。cell默认的宽度是屏幕宽度,所以我们只需要把cell的宽度设置小一点,然后在加上间距就可以了。

    -(void)setItemWidth:(CGFloat)itemWidth
    {
        _itemWidth = itemWidth;
        self.flowLayout.itemSize = CGSizeMake(itemWidth, self.bounds.size.height);
    }
    -(void)setItemSpace:(CGFloat)itemSpace
    {
        _itemSpace = itemSpace;
        self.flowLayout.minimumLineSpacing = itemSpace;
    }
    

    然后设置图片默认位置,注意移动方式的参数是UICollectionViewScrollPositionCenteredHorizontally表示横向移动将cell移到屏幕中间

     [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targeIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    

    然后计算当前index的逻辑需要修改,以前的一页是cell的宽度,现在因为有间距了所以一页是cell的宽度加上cell间的空隙

    -(NSInteger)currentIndex
    {
        if(self.collectionView.frame.size.width == 0 || self.collectionView.frame.size.height == 0)
            return 0;
        NSInteger index = 0;
        
        if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {//水平滑动
            index = (self.collectionView.contentOffset.x + (self.itemWidth + self.itemSpace) * 0.5) / (self.itemSpace + self.itemWidth);
        }else{
            index = (self.collectionView.contentOffset.y + _flowLayout.itemSize.height * 0.5)/ _flowLayout.itemSize.height;
        }
        return MAX(0,index);
        
    }
    

    最后就是手动滑动的时候,位移一个cell宽度加间隙的距离,其实只要算对了index,直接位移过去就好。

    //手离开屏幕的时候
    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
        //如果是向右滑或者滑动距离大于item的一半,则像右移动一个item+space的距离,反之向左
        float currentPoint = scrollView.contentOffset.x;
        float moveWidth = currentPoint-_oldPoint;
        int shouldPage = moveWidth/(self.itemWidth/2);
        if (velocity.x>0 || shouldPage > 0) {
            _dragDirection = 1;
        }else if (velocity.x<0 || shouldPage < 0){
            _dragDirection = -1;
        }else{
            _dragDirection = 0;
        }
    }
        //松开手指滑动开始减速的时候,设置滑动动画
    - (void)scrollViewWillBeginDecelerating: (UIScrollView *)scrollView{
        NSInteger currentIndex = (_oldPoint + (self.itemWidth + self.itemSpace) * 0.5) / (self.itemSpace + self.itemWidth);
        [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:currentIndex + _dragDirection inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    
    }
    

    这样有间距的无限轮播就完成,但是需求中还要中间的图片放大,两边的图片缩小,滑动的时候,滑到中间是最大的。
    其实这个也比较简单,只要根据cell距离屏幕中间多远来设置cell缩放的大小,中间最大,两边最小。在哪里设置呢,这里要继承UICollectionViewFlowLayout,然后重写layoutAttributesForElementsInRect方法

    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
        // 1.获取cell对应的attributes对象
        NSArray* arrayAttrs = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];    
        if(!self.isZoom) return arrayAttrs;
    
        // 2.计算整体的中心点的x值
        CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width * 0.5;
        
        // 3.修改一下attributes对象
        for (UICollectionViewLayoutAttributes *attr in arrayAttrs) {
            // 3.1 计算每个cell的中心点距离
            CGFloat distance = ABS(attr.center.x - centerX);
            
            // 3.2 距离越大,缩放比越小,距离越小,缩放比越大
            CGFloat factor = 0.001;
            CGFloat scale = 1 / (1 + distance * factor);
            attr.transform = CGAffineTransformMakeScale(scale, scale);
        }
        return arrayAttrs;
    }
    

    这样就搞定了。是不是很简单。不废话了直接给项目

    相关文章

      网友评论

        本文标题:有间距的无限轮播图

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