参考 SDCycleScrollView
轮播框架的巧妙设计,自己动手实现啦类似的需求。

-
需求如图,实现无限轮播效果,跟纯图片不同的是,就是内容多了点而已,没办法没有现成的框架,只有按着轮播思路写一个。
-
实现这种需求,需要克服以下几点:
- 自动滚动轮播
- 滚动到最后一个返回第一个,且过渡要自然
- 计算当前显示页码
界面构成UICollectionView + UIPageControl
选用CollectionView因为用复用机制巧妙的实现无限轮播且不必担心内存开销过大,继承自ScrollView,拥有ScrollView的全部特性。
解决自动滚动 - 倒计时的应用
- 数据为1个时,特殊处理不滚动
if (_arrayData.count > 1) {
self.collectionView.scrollEnabled = YES;
//处理是否自动滑动,定时器问题
[self setAutoScroll:self.autoScroll];
}else{
self.collectionView.scrollEnabled = NO;
[self setAutoScroll:NO];
}
- 控制是否自动滚动
- (void)setAutoScroll:(BOOL)autoScroll {
_autoScroll = autoScroll;
//创建之前,停止定时器
[self invalidateTimer];
if (_autoScroll) {
[self setupTimer];
}
}
- //停止定时器
- (void)invalidateTimer
{
[_timer invalidate];
_timer = nil;
}
- //开启定时器
- (void)setupTimer
{
[self invalidateTimer]; // 创建定时器前先停止定时器,不然会出现僵尸定时器,导致轮播频率错误
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoScrollTimeInterval target:self selector:@selector(automaticScroll) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
_timer = timer;
}
*//定时器调用
- (void)automaticScroll {
if (0 == _totalItems) {
return;
}
NSInteger currentIndex = [self currentIndex];
NSInteger targetIndex = currentIndex + 1;
[self scrollToIndex:targetIndex];
}
- // 滚动位置计算
- (void)scrollToIndex:(NSInteger)targetIndex{
if (targetIndex >= _totalItems) {//调到中间的任意一组里面的 第0个图片
if (self.infiniteLoop) {//无限循环
targetIndex = _totalItems * 0.5;
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}
return;
}
//滚动到指定位置,打开系统默认滚动动画,看到过渡效果
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}
- //计算当前index
- (NSInteger)currentIndex {
if (_collectionView.frame.size.width == 0 || _collectionView.frame.size
.height == 0) {
return 0;
}
NSInteger index = 0;
if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {//水平滑动
index = (_collectionView.contentOffset.x + _flowLayout.itemSize.width * 0.5) / _flowLayout.itemSize.width;
}else{
index = (_collectionView.contentOffset.y + _flowLayout.itemSize.height * 0.5)/ _flowLayout.itemSize.height;
}
//返回两个数中的最大值
return MAX(0,index);
}
无限轮播实现 —— item数据乘以100
- (void)setArrayData:(NSArray *)arrayData
{
_arrayData = arrayData;
//pageControl的显示个数
_pageControl.numberOfPages = _arrayData.count;
// 这一句是关键,如果是无限循环,就让 item 的总数乘以 100 。
_totalItems = self.infiniteLoop ? arrayData.count * 100 : arrayData.count;
if (_arrayData.count > 1) {
self.collectionView.scrollEnabled = YES;
//处理是否自动滑动,定时器问题
[self setAutoScroll:self.autoScroll];
}else{
self.collectionView.scrollEnabled = NO;
[self setAutoScroll:NO];
}
[self.collectionView reloadData];
}
- item 的总数乘以 100,为啦让手动轮播时,可以无限翻下去,虽然可以翻到最后一张就翻不动啦,应该不会有人这么无聊吧!
使用pagecontrol 如何准确显示页码
- UIScrollViewDelegate
滚动时根据偏移量,得到当前item位置和pageControl显示页码
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (!self.arrayData.count) return; // 解决清除timer时偶尔会出现的问题
int itemIndex = (int)[self currentIndex];
int indexOnPageControl = [self pageControlIndexWithCurrentCellIndex:itemIndex];
UIPageControl *pageControl = (UIPageControl *)_pageControl;
pageControl.currentPage = indexOnPageControl;
}
优化:开始拖动时停止定时器
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (self.autoScroll) {
[self invalidateTimer];
}
}
拖动结束时开启定时器
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (self.autoScroll) {
[self setupTimer];
}
}
- 取余计算得到当前页码
- (int)pageControlIndexWithCurrentCellIndex:(NSInteger)index {
return (int)index % self.arrayData.count;
}
移除定时器
- 子视图将要移除时调用
- (void)willMoveToSuperview:(UIView *)newSuperview {
[super willMoveToSuperview:newSuperview];
if (! newSuperview && self.timer) {
// 销毁定时器
[self.timer invalidate];
self.timer = nil;
}
}
网友评论