iOS 兩種不同的圖片無限輪播

前記

其實想寫這個關(guān)于無限輪播的記錄已經(jīng)很久很久了型奥,只是沒什么時間,這只是一個借口碉京,正如:時間就像海綿厢汹,擠一擠還是有的。記得在剛剛開始工作的時候谐宙,第一個接觸到比較酷的東西就是圖片的無限輪播烫葬,那還是三年多前的一個火辣辣的夏天,其實可以說是秋天凡蜻,然而天府之國的成都并沒有....下面我們進入正題吧

輪播的方式

到目前為止搭综,我見過的輪播方法,大概有那么三種咽瓷,由于輪播嘛设凹,所以肯定都是在UIScrollView的基礎(chǔ)上

1、在UIScrollView上添加N+2UIImageView
2茅姜、在UIScrollView上添加固定的3UIImageView
3闪朱、利用重用機制中的UICollectionView來實現(xiàn)

原理

下面,就簡單闡述下三種不同方法的原理钻洒,以及其優(yōu)缺點

  • 第一種:其原理圖大概如下
N+1.png

該方案的原理是在UIScrollView上添加N+2UIImageView奋姿,比如上圖中,有三張需要輪播的圖素标,那么我們可以添加五個UIImageViewUIScrollView上称诗,圖片賦值的順序如上圖,當我們向左滑動到最左的時候头遭,即到3這個位置的時候寓免,調(diào)用setContentOffset癣诱,讓其滾動到右邊的3這個位置,當然不能調(diào)用動畫袜香,這樣肉眼是看不出來的撕予,給我們造成一種視角錯覺,如果是向右滑動到最右邊的1蜈首,其原理也是一樣的实抡。

優(yōu)點:容易理解
缺點:如果有100個圖片,那么我們豈不是要添加102個欢策,這樣的話吆寨,內(nèi)存肯定是吃不消的把。

  • 第二種:其原理圖大概如下
3.png

該方案的原理是在UIScrollView上添加固定的3UIImageView踩寇,在初始化的時候啄清,分別如上圖那樣賦值,并且調(diào)用setContentOffset姑荷,讓其居中盒延。of course,這只是前奏鼠冕,對比第一種方法添寺,肯定在邏輯處理上復(fù)雜點。
復(fù)雜邏輯懈费,當我們向左或者向右滑動一張圖片后计露,需要根據(jù)當前滾動的index來設(shè)置圖片,并且由于滑動后UIScrollViewcontentOffset發(fā)生了改變憎乙,后續(xù)還需要處理一些其他邏輯票罐。
比如向左滑動的話,就到了最右邊泞边,這樣该押,我們再向右就不能再滑動了,所以為了保證能繼續(xù)滑動阵谚,我們需要在滑動結(jié)束的時候蚕礼,設(shè)置contentOffset,使第二個UIImageView一直處于屏幕中間梢什,除此之外奠蹬,我們還需要重新設(shè)置圖片,由于我們向左滑動嗡午,圖中的2顯示的圖片其實是我們需要展示的囤躁,因為我們要設(shè)置contentOffset,這樣2這個imageView就又移動到最右邊去了,所以這時候我們設(shè)置圖片狸演,就要將1imageView設(shè)置為后面一張即2imageView的圖片言蛇,將3imageView設(shè)置為1之前的圖片,如此來實現(xiàn)循環(huán)宵距。
下面是部分代碼

//減速停止的時候
-(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

優(yōu)點:相比第一種猜极,內(nèi)存上開銷很小
缺點:代碼稍多,理解復(fù)雜消玄,如果非常快速滑動丢胚,可能會出現(xiàn)最右或者最左滑不動翩瓜,因為scrollViewDidEndDecelerating還未執(zhí)行

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

collectionview.png

其原理很簡單,主要是根據(jù)UICollectionView的重用機制携龟,通過創(chuàng)建許多個cell兔跌,然后來實現(xiàn),當然峡蟋,內(nèi)存就不用去考慮了坟桅,因為這個是通過重用機制實現(xiàn)的。

下面是部分代碼

創(chuàng)建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];
}

//關(guān)閉定時器
- (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;
    
    //下面的第二個條件 可以確保 盡量一次去執(zhí)行block 而不多次
    if (self.rollingDidScrollBlock && fabs(fIndex - (CGFloat)index) <= 0.00001)
    {
//            NSLog(@" 打印信息:%ld",(long)currentIndex);
        
        self.rollingDidScrollBlock(currentIndex);
    }
    
}

在上面代理scrollViewDidScroll中蕊蝗,有個關(guān)鍵地方仅乓,大家都知道scrollViewDidScroll只要再滑動過程中就會一直執(zhí)行,為了避免多次執(zhí)行蓬戚,而導(dǎo)致內(nèi)存問題夸楣,我們希望的是盡可能的在滑動結(jié)束的時候來執(zhí)行,所以這個地方子漩,加了一句判斷fabs(fIndex - (CGFloat)index) <= 0.00001豫喧,因為在結(jié)束的時候,這兩個值的差應(yīng)該很小幢泼,幾乎可以為0紧显。所以,這樣就不會導(dǎo)致代碼在此多次執(zhí)行缕棵。

文章結(jié)尾

針對上的三種方法孵班,我對后面兩種方式寫了兩個簡單的demo,下面附上demo地址挥吵,希望對大家有幫助
第二種
第三種

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末重父,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子忽匈,更是在濱河造成了極大的恐慌房午,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丹允,死亡現(xiàn)場離奇詭異郭厌,居然都是意外死亡袋倔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門折柠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宾娜,“玉大人,你說我怎么就攤上這事扇售∏八” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵承冰,是天一觀的道長华弓。 經(jīng)常有香客問我,道長困乒,這世上最難降的妖魔是什么寂屏? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮娜搂,結(jié)果婚禮上迁霎,老公的妹妹穿的比我還像新娘。我一直安慰自己百宇,他們只是感情好考廉,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著携御,像睡著了一般芝此。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上因痛,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天婚苹,我揣著相機與錄音,去河邊找鬼鸵膏。 笑死膊升,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谭企。 我是一名探鬼主播廓译,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼债查!你這毒婦竟也來了非区?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盹廷,失蹤者是張志新(化名)和其女友劉穎征绸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡管怠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年淆衷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渤弛。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡祝拯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出她肯,到底是詐尸還是另有隱情佳头,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布晴氨,位于F島的核電站畜晰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瑞筐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一腊瑟、第九天 我趴在偏房一處隱蔽的房頂上張望聚假。 院中可真熱鬧,春花似錦闰非、人聲如沸膘格。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘪贱。三九已至,卻和暖如春辆毡,著一層夾襖步出監(jiān)牢的瞬間菜秦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工舶掖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留球昨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓眨攘,卻偏偏與公主長得像主慰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鲫售,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容