有间距的无限轮播图

最近需求中需要用到中间有间隔的无限轮播图,中间还要放大。所以仿照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 *)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;
}

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

你可能感兴趣的:(有间距的无限轮播图)