代碼地址以下:
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大師代發,拒絕轉載,轉載須要做者受權