iOS 类似腾讯视频轮播器+缩放效果

在最近公司项目需求,要求实现类似于腾讯视频轮播器的效果,另外需要加上缩放效果,先上实现的效果图

定时器效果

手动拖拽效果

手动拖拽的时候可以看到随着拖动的操作,图片有个放大的效果,以上就是所要实现的最终效果。

可能看到这里,你会想到 UICollectionView,自定义布局,每次滑动结束的时候,将距离屏幕中心位置最近的Item 显示在中间,然后将显示在屏幕中的 Item 进行设置 transform属性。
在一开始看到这个需求的时候,也没有考虑到这么多,认为一个UICollectionView就能搞定这个需求。可是当自己把自定义布局实现了,然后鼠标随手一拽,速度一块,发现有多个 Item 已经随着滚动过去了,这明显不能满足需求,如果将pageEnable 属性设置为 YES,虽然可以不让多个 Item 滚动过去,但是UICollectionViewcontentOffset又不正确,默认的分页宽度为UICollectionView的宽度。于是在网上找到方案:第一种是将 UIScrollView的宽度设置为我们想要的每页的偏移宽度,然后将UIScrollViewclipsToBounds设置为 NO,这样每次滑动就能达到我们想要的偏移量。第二种是完全自定义分页宽度。
先说说第一种方法:
在将UICollectionViewframe设置为想要的偏移量大小时,因为复用机制,每次滑出 collectionView 范围时,就被回收了,而需求是要显示三张图片,于是转战UIScrollView,暂时不实现缩放效果。一开始在scrollViewDidScroll:代理里去设置显示的图片,ScrollView各种不按常理去偏移,最后在scrollViewDidEndDecelerating:代理方法去实现图片的显示和偏移量。

定时器效果:

定时器看起来,没毛病,可是等拖拽的时候就懵逼了,因为一滑的快的话就有问题了。当滑到最后一张的时候因为代理方法的调用时间,所以有可能来不及设置回到第一张图片。

后面由于种种原因就又回到了UICollectionView,在这里用的就是第二种方法完全自定义分页的宽度。

- (void)snapToPage {
    CGPoint pageOffset;
    pageOffset.x = [self pageOffsetForComponent:YES];
    pageOffset.y = [self pageOffsetForComponent:NO];


    CGPoint currentOffset = self.contentOffset;

    if (!CGPointEqualToPoint(pageOffset, currentOffset)) {
        _snapping = YES;

        [self setContentOffset:pageOffset animated:YES];
    }


    _dragVelocity = CGPointZero;
    _dragDisplacement = CGPointZero;
}
- (CGFloat)pageOffsetForComponent:(BOOL)isX {
    if (((isX ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds)) == 0) || ((isX ? self.contentSize.width : self.contentSize.height) == 0))
        return 0;


    CGFloat pageLength = isX ? _pageWidth : _pageHeight;

    if (pageLength < FLT_EPSILON)
        pageLength = isX ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds);

    pageLength *= self.zoomScale;


    CGFloat totalLength = isX ? self.contentSize.width : self.contentSize.height;

    CGFloat visibleLength = (isX ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds)) * self.zoomScale;

    CGFloat currentOffset = isX ? self.contentOffset.x : self.contentOffset.y;

    CGFloat dragVelocity = isX ? _dragVelocity.x : _dragVelocity.y;

    CGFloat dragDisplacement = isX ? _dragDisplacement.x : _dragDisplacement.y;


    CGFloat newOffset;


    CGFloat index = currentOffset / pageLength;

    CGFloat lowerIndex = floorf(index);
    CGFloat upperIndex = ceilf(index);

    if (ABS(dragDisplacement) < DRAG_DISPLACEMENT_THRESHOLD || dragDisplacement * dragVelocity < 0) {
        if (index - lowerIndex > upperIndex - index) {
            index = upperIndex;
        } else {
            index = lowerIndex;
        }
    } else {
        if (dragVelocity > 0) {
            index = upperIndex;
        } else {
            index = lowerIndex;
        }
    }


    newOffset = pageLength * index;

    if (newOffset > totalLength - visibleLength)
        newOffset = totalLength - visibleLength;

    if (newOffset < 0)
        newOffset = 0;


    return newOffset;
}
#pragma mark - ScrollView delegate

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _dragDisplacement = scrollView.contentOffset;

    if (_delegateRespondsToWillBeginDragging)
        [_actualDelegate scrollViewWillBeginDragging:scrollView];
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (_pagingEnabled) {
        *targetContentOffset = scrollView.contentOffset;


        _dragVelocity = velocity;

        _dragDisplacement = CGPointMake(scrollView.contentOffset.x - _dragDisplacement.x, scrollView.contentOffset.y - _dragDisplacement.y);
    } else {
        if (_delegateRespondsToWillEndDragging)
            [_actualDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate && _pagingEnabled)
        [self snapToPage];


    if (_delegateRespondsToDidEndDragging)
        [_actualDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (_pagingEnabled)
        [self snapToPage];


    if (_delegateRespondsToDidEndDecelerating)
        [_actualDelegate scrollViewDidEndDecelerating:scrollView];
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    if (!_snapping && _pagingEnabled) {
        [self snapToPage];
    } else {
        _snapping = NO;
    }


    if (_delegateRespondsToDidEndScrollingAnimation)
        [_actualDelegate scrollViewDidEndScrollingAnimation:scrollView];
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
    if (_pagingEnabled)
        [self snapToPage];

    if (_delegateRespondsToDidEndZooming)
        [_actualDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
}

以上的代码都是在 网上 查找到的,最后实现了想要的效果。由于这里我用的是UICollcetionView,所以就直接自定义了一个GPCollectionView继承于UICollectionView,最后却发现UICollectionViewDelegate的方法始终不会调用,UIScrollViewDelegate的方法却能调用,在此希望大神们能解释一下。

最后附上代码
利用 ScrollView 实现分页效果
利用 UICollectionView 实现分页效果

IT文库 » iOS 类似腾讯视频轮播器+缩放效果
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址