iOS 兩種不一樣的圖片無限輪播

代碼地址以下:
http://www.demodashi.com/demo/11608.htmlhtml

前記

其實想寫這個關於無限輪播的記錄已經好久好久了,只是沒什麼時間,這只是一個藉口,正如:時間就像海綿,擠一擠仍是有的。記得在剛剛開始工做的時候,第一個接觸到比較酷的東西就是圖片的無限輪播,那仍是三年多前的一個火辣辣的夏天,其實能夠說是秋天,然而天府之國的成都並無....下面咱們進入正題吧oop

輪播的方式

到目前爲止,我見過的輪播方法,大概有那麼三種,因爲輪播嘛,因此確定都是在UIScrollView的基礎上動畫

1、在UIScrollView上添加N+2UIImageView
2、在UIScrollView上添加固定的3UIImageView
3、利用重用機制中的UICollectionView來實現代理

原理

下面,就簡單闡述下三種不一樣方法的原理,以及其優缺點code

  • 第一種:其原理圖大概以下

N+1.png

該方案的原理是在UIScrollView 上添加N+2UIImageView ,好比上圖中,有三張須要輪播的圖,那麼咱們能夠添加五個UIImageViewUIScrollView上,圖片賦值的順序如上圖,當咱們向左滑動到最左的時候,即到3這個位置的時候,調用setContentOffset ,讓其滾動到右邊的3這個位置,固然不能調用動畫,這樣肉眼是看不出來的,給咱們形成一種視角錯覺,若是是向右滑動到最右邊的1,其原理也是同樣的。htm

優勢:容易理解
缺點:若是有100個圖片,那麼咱們豈不是要添加102個,這樣的話,內存確定是吃不消的把。blog

  • 第二種:其原理圖大概以下

3.png

該方案的原理是在UIScrollView 上添加固定的3UIImageView ,在初始化的時候,分別如上圖那樣賦值,而且調用setContentOffset,讓其居中。of course,這只是前奏,對比第一種方法,確定在邏輯處理上覆雜點。
複雜邏輯,當咱們向左或者向右滑動一張圖片後,須要根據當前滾動的index來設置圖片,而且因爲滑動後UIScrollViewcontentOffset發生了改變,後續還須要處理一些其餘邏輯。
好比向左滑動的話,就到了最右邊,這樣,咱們再向右就不能再滑動了,因此爲了保證能繼續滑動,咱們須要在滑動結束的時候,設置contentOffset,使第二個UIImageView一直處於屏幕中間,除此以外,咱們還須要從新設置圖片,因爲咱們向左滑動,圖中的2顯示的圖片實際上是咱們須要展現的,由於咱們要設置contentOffset,這樣2這個imageView就又移動到最右邊去了,因此這時候咱們設置圖片,就要將1imageView設置爲後面一張即2imageView的圖片,將3imageView設置爲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);
    }
}

效果如以下:內存

輪播1.gif

優勢:相比第一種,內存上開銷很小
缺點:代碼稍多,理解複雜,若是很是快速滑動,可能會出現最右或者最左滑不動,由於scrollViewDidEndDecelerating還未執行ci

  • 第三種:其原理圖大概以下

collectionview.png
其原理很簡單,主要是根據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 兩種不一樣的圖片無限輪播

代碼地址以下:
http://www.demodashi.com/demo/11608.html

注:本文著做權歸做者,由demo大師代發,拒絕轉載,轉載須要做者受權

相關文章
相關標籤/搜索