代码地址以下:
http://www.demodashi.com/demo/11608.htmlhtml
其实想写这个关于无限轮播的记录已经好久好久了,只是没什么时间,这只是一个借口,正如:时间就像海绵,挤一挤仍是有的
。记得在刚刚开始工做的时候,第一个接触到比较酷的东西就是图片的无限轮播,那仍是三年多前的一个火辣辣的夏天,其实能够说是秋天,然而天府之国的成都并无....下面咱们进入正题吧oop
到目前为止,我见过的轮播方法,大概有那么三种,因为轮播嘛,因此确定都是在UIScrollView
的基础上动画
1、在UIScrollView
上添加N+2
个UIImageView
2、在UIScrollView
上添加固定的3
个UIImageView
3、利用重用机制中的UICollectionView
来实现代理
下面,就简单阐述下三种不一样方法的原理,以及其优缺点code
该方案的原理是在UIScrollView
上添加N+2
个UIImageView
,好比上图中,有三张须要轮播的图,那么咱们能够添加五个UIImageView
在UIScrollView
上,图片赋值的顺序如上图,当咱们向左滑动到最左的时候,即到3
这个位置的时候,调用setContentOffset
,让其滚动到右边的3
这个位置,固然不能调用动画,这样肉眼是看不出来的,给咱们形成一种视角错觉,若是是向右滑动到最右边的1
,其原理也是同样的。htm
优势:容易理解
缺点:若是有100
个图片,那么咱们岂不是要添加102
个,这样的话,内存确定是吃不消的把。blog
该方案的原理是在UIScrollView
上添加固定的3
个UIImageView
,在初始化的时候,分别如上图那样赋值,而且调用setContentOffset
,让其居中。of course,这只是前奏,对比第一种方法,确定在逻辑处理上复杂点。
复杂逻辑,当咱们向左或者向右滑动一张图片后,须要根据当前滚动的index
来设置图片,而且因为滑动后UIScrollView
的contentOffset
发生了改变,后续还须要处理一些其余逻辑。
好比向左滑动的话,就到了最右边,这样,咱们再向右就不能再滑动了,因此为了保证能继续滑动,咱们须要在滑动结束的时候,设置contentOffset
,使第二个UIImageView
一直处于屏幕中间,除此以外,咱们还须要从新设置图片,因为咱们向左滑动,图中的2
显示的图片实际上是咱们须要展现的,由于咱们要设置contentOffset
,这样2
这个imageView
就又移动到最右边去了,因此这时候咱们设置图片,就要将1
的imageView
设置为后面一张即2
的imageView
的图片,将3
的imageView
设置为1
以前的图片,如此来实现循环。
下面是部分代码图片
//减速中止的时候 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { _endOffsetX = scrollView.contentOffset.x; //给imageview赋值 [self loadImage]; //改变offset [_scrollView setContentOffset:CGPointMake(self.bounds.size.width, 0) animated:NO]; if (self.didScrollToIndexBlock) { self.didScrollToIndexBlock(_currentIndex); } }
效果如以下:内存
优势:相比第一种,内存上开销很小
缺点:代码稍多,理解复杂,若是很是快速滑动,可能会出现最右或者最左滑不动,由于scrollViewDidEndDecelerating
还未执行ci
其原理很简单,主要是根据UICollectionView
的重用机制,经过建立许多个cell
,而后来实现,固然,内存就不用去考虑了,由于这个是经过重用机制实现的。
下面是部分代码
建立UICollectionView
- (void)addCollectionView { self.collectionFlowLayout = [[UICollectionViewFlowLayout alloc] init]; self.collectionFlowLayout.minimumLineSpacing = 0; self.collectionFlowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; self.collectionFlowLayout.itemSize = self.bounds.size; self.collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:self.collectionFlowLayout]; self.collectionView.dataSource = (id)self; self.collectionView.delegate = (id)self; self.collectionView.pagingEnabled = YES; self.collectionView.showsVerticalScrollIndicator = self.collectionView.showsHorizontalScrollIndicator = NO; [self.collectionView registerClass:[GLRollingScrollviewCell class] forCellWithReuseIdentifier:GLRollingScrollviewCellId]; [self addSubview:self.collectionView]; }
自动滚动定时器部分
//开启定时器 - (void)startTimer { [self cofigTimer]; } //关闭定时器 - (void)pauseTimer { if (self.timer) { CFRunLoopTimerInvalidate(self.timer); CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), self.timer, kCFRunLoopCommonModes); } } //配置定时器 - (void)cofigTimer { if (self.imageUrlArray.count <= 1) { return; } if (self.timer) { CFRunLoopTimerInvalidate(self.timer); CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), self.timer, kCFRunLoopCommonModes); } __weak typeof(self)weakSelf = self; CFRunLoopTimerRef time = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+ _intervalTimer, _intervalTimer, 0, 0, ^(CFRunLoopTimerRef timer) { [weakSelf autoScroll]; }); self.timer = time; CFRunLoopAddTimer(CFRunLoopGetCurrent(), time, kCFRunLoopCommonModes); } //自动滚动 - (void)autoScroll { NSInteger currentIndex = (self.collectionView.contentOffset.x + self.collectionFlowLayout.itemSize.width * 0.5) / self.collectionFlowLayout.itemSize.width; NSInteger toIndex = currentIndex + 1; NSIndexPath *indexPath = nil; if (toIndex == self.totalNumber) { toIndex = self.totalNumber * 0.5; indexPath = [NSIndexPath indexPathForRow:toIndex inSection:0]; [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:NO]; } else { indexPath = [NSIndexPath indexPathForItem:toIndex inSection:0]; [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:YES]; } }
手动滑动时避免和定时器冲突
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self pauseTimer]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { [self startTimer]; }
计算当前index
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (self.totalNumber == 0) { return; } NSInteger currentIndex = (scrollView.contentOffset.x + self.collectionView.frame.size.width * 0.5) / self.collectionView.frame.size.width;; currentIndex = currentIndex % self.imageUrlArray.count; CGFloat x = scrollView.contentOffset.x - self.collectionView.frame.size.width; NSUInteger index = fabs(x) / self.collectionView.frame.size.width; CGFloat fIndex = fabs(x) / self.collectionView.frame.size.width; //下面的第二个条件 能够确保 尽可能一次去执行block 而很少次 if (self.rollingDidScrollBlock && fabs(fIndex - (CGFloat)index) <= 0.00001) { // NSLog(@" 打印信息:%ld",(long)currentIndex); self.rollingDidScrollBlock(currentIndex); } }
在上面代理scrollViewDidScroll
中,有个关键地方,你们都知道scrollViewDidScroll
只要再滑动过程当中就会一直执行,为了不屡次执行,而致使内存问题,咱们但愿的是尽量的在滑动结束的时候来执行,因此这个地方,加了一句判断fabs(fIndex - (CGFloat)index) <= 0.00001
,由于在结束的时候,这两个值的差应该很小,几乎能够为0。因此,这样就不会致使代码在此屡次执行。
针对上的三种方法,我对后面两种方式写了两个简单的demo
,但愿对你们有帮助。iOS 两种不一样的图片无限轮播
注:本文著做权归做者,由demo大师代发,拒绝转载,转载须要做者受权