iOS 类似腾讯视频轮播器+缩放效果
在最近公司项目需求,要求实现类似于腾讯视频轮播器的效果,另外需要加上缩放效果,先上实现的效果图
![](https://img.haomeiwen.com/i662079/5c3f43453c29032d.png)
定时器效果
![](https://img.haomeiwen.com/i662079/ff455e9693c8c7a6.gif)
手动拖拽效果
![](https://img.haomeiwen.com/i662079/c4f605fcc10e9e72.gif)
手动拖拽的时候可以看到随着拖动的操作,图片有个放大的效果,以上就是所要实现的最终效果。
可能看到这里,你会想到 UICollectionView
,自定义布局,每次滑动结束的时候,将距离屏幕中心位置最近的Item 显示在中间,然后将显示在屏幕中的 Item 进行设置 transform
属性。
在一开始看到这个需求的时候,也没有考虑到这么多,认为一个UICollectionView
就能搞定这个需求。可是当自己把自定义布局实现了,然后鼠标随手一拽,速度一块,发现有多个 Item 已经随着滚动过去了,这明显不能满足需求,如果将pageEnable
属性设置为 YES,虽然可以不让多个 Item 滚动过去,但是UICollectionView
的contentOffset
又不正确,默认的分页宽度为UICollectionView
的宽度。于是在网上找到方案:第一种是将 UIScrollView
的宽度设置为我们想要的每页的偏移宽度,然后将UIScrollView
的clipsToBounds
设置为 NO,这样每次滑动就能达到我们想要的偏移量。第二种是完全自定义分页宽度。
先说说第一种方法:
在将UICollectionView
的 frame
设置为想要的偏移量大小时,因为复用机制,每次滑出 collectionView
范围时,就被回收了,而需求是要显示三张图片,于是转战UIScrollView
,暂时不实现缩放效果。一开始在scrollViewDidScroll:
代理里去设置显示的图片,ScrollView
各种不按常理去偏移,最后在scrollViewDidEndDecelerating:
代理方法去实现图片的显示和偏移量。
定时器效果:
![](https://img.haomeiwen.com/i662079/e6377864b5bb7ab2.gif)
定时器看起来,没毛病,可是等拖拽的时候就懵逼了,因为一滑的快的话就有问题了。当滑到最后一张的时候因为代理方法的调用时间,所以有可能来不及设置回到第一张图片。
![](https://img.haomeiwen.com/i662079/1538c6f790bd4ddf.gif)
后面由于种种原因就又回到了
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 实现分页效果