做一個廣告無限輪播的UICollectionView組件吧

前言

廣告無限輪播跌穗,隨處可見的功能,也有很多iOS Developer實(shí)現(xiàn)過這個組件瞻离,在這說一下我的實(shí)現(xiàn)方式乒裆,算是一個總結(jié)吧。

這個功能有兩種實(shí)現(xiàn)方式肉迫,老的做法是使用UIScrollView稿黄,這個方法可能需要多寫一些邏輯代碼;新的方法就是使用UICollectionView族购,因?yàn)閁ICollectionView的一些特性陵珍,相對于UIScrollView在代碼和邏輯上能夠省去不少功夫。本文著重介紹的就是使用UICollectionView實(shí)現(xiàn)無限輪播的功能互纯。

原理

在最前面多添加最后一張圖片,在最后面多添加第一張圖片只盹,圖片和cell個數(shù)都+2,然后在滑動到cell(0)時換到cell(n-1)殖卑,滑動到cell(n)時換到cell(1),因?yàn)閷?yīng)的cell上的圖片是相同的钻哩,在切換的時候取消動畫效果肛冶,這樣在視覺上是看不出來的。

實(shí)現(xiàn)過程

原理其實(shí)很簡單睦袖。正常情況下馅笙,應(yīng)該是多少張圖片對應(yīng)多少個cell,也就是說圖片的數(shù)量和cell的數(shù)量保持一致董习,但是這樣就無法實(shí)現(xiàn)輪播的效果了。我們采取的辦法是招刹,先處理圖片窝趣,在第一張圖片前面添加一張最后一張圖片,然后在最后一張圖片后面添加一張第一張圖片哑舒,這樣圖片的個數(shù)就+2了,創(chuàng)建的cell響應(yīng)的也+2越锈,假設(shè)我們要展示2張圖片膘滨,經(jīng)過處理后,應(yīng)該是這個樣子的:

2張圖片要對應(yīng)4個cell

對于初始數(shù)組的處理代碼对蒲,我們可以這么寫:
- (NSArray *)reloadDataArrayWithDataArray:(NSArray *)dataArray {
NSMutableArray *resultArray = [[NSMutableArray alloc] initWithCapacity:dataArray. count];
[resultArray addObject:dataArray.lastObject];

    for (id object in dataArray) {
        [resultArray addObject:object];
    }
    
    [resultArray addObject:dataArray.firstObject];
    return resultArray;
}

如果這樣的話,默認(rèn)顯示的就是image2了砰逻,而不是我們想要的image1泛鸟,這時候在初始化的時候我們需要做點(diǎn)小手腳,通過下面的方法換到cell1就可以了

[self.carouselCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_currentItem inSection:0]
                                    atScrollPosition:UICollectionViewScrollPositionNone
                                            animated:NO];

經(jīng)過處理后北滥,應(yīng)該是這個樣子的:

紅框中的cell為默認(rèn)顯示內(nèi)容

OK再芋!按照之前的過程,我們已經(jīng)可以看到這樣的效果了济赎,默認(rèn)進(jìn)來顯示的image1,無論向左還是向右滑動构捡,顯示的都是image2壳猜,貌似是我們想要的輪播效果,但是繼續(xù)滑動總會有到頭的時候喘帚,而我們想要有輪播效果闪幽,就必須保證當(dāng)前顯示的cell不是第一個或者最后一個涡匀,也就是說當(dāng)前顯示的cell前后必須都有cell,這樣才可以在滑動的時候不會出現(xiàn)到邊緣無法滑動的情況腕够。上圖中舌劳,只有cell1、cell2才是可以輪播的位置甚淡,那么我們只需要在滑動到cell0的時候換到cell2、滑動到cell3的時候換到cell1资柔,還是之前用過的方法來實(shí)現(xiàn):

if (self.currentItem == 0) {
    toItem = self.dataArray.count-2;
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:toItem inSection:0];
    [self.carouselCollectionView scrollToItemAtIndexPath:indexPath
                                        atScrollPosition:UICollectionViewScrollPositionNone
                                                animated:NO];
    self.currentItem = toItem;
}

if (self.currentItem == self.dataArray.count-1) {
    toItem = 1;
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:toItem inSection:0];
    [self.carouselCollectionView scrollToItemAtIndexPath:indexPath
                                        atScrollPosition:UICollectionViewScrollPositionNone
                                                animated:NO];
    self.currentItem = toItem;
}

那么我們在什么時候進(jìn)行這個交換cell的操作呢?UIScrollViewDelegate的代理方法:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self reloadItemAction];
}

- (void)reloadItemAction {
    NSIndexPath *indexPath = [[self.carouselCollectionView indexPathsForVisibleItems]   lastObject];
    self.currentItem = indexPath.item;
    NSInteger toItem = 0;
    
    if (self.currentItem == 0) {
        toItem = self.dataArray.count-2;
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:toItem inSection:0];
        [self.carouselCollectionView scrollToItemAtIndexPath:indexPath
                                            atScrollPosition:UICollectionViewScrollPositi   onNone
                                                    animated:NO];
        self.currentItem = toItem;
    }
    
    if (self.currentItem == self.dataArray.count-1) {
        toItem = 1;
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:toItem inSection:0];
        [self.carouselCollectionView scrollToItemAtIndexPath:indexPath
                                            atScrollPosition:UICollectionViewScrollPositi   onNone
                                                    animated:NO];
        self.currentItem = toItem;
    }
    
    self.pageControl.currentPage = self.currentItem-1;
}

看完剛才的代碼,你會有疑問羹与,self.pageControl,這個什么鬼吃衅?對于這個組件來說腾誉,只顯示圖片肯定是不行的,我們還需要UIPageControl來顯示當(dāng)前圖片的位置妄辩,也就是說當(dāng)前顯示的是第幾張圖片。因?yàn)槲覀兊腸ell數(shù)量實(shí)際上是比傳入的圖片數(shù)量+2的英支,所以我們在操作UIPageControlcurrentPage時哮伟,需要注意這一點(diǎn)。
還有我們會需要點(diǎn)擊某一張圖片進(jìn)行詳情的查看或者跳轉(zhuǎn)到一個其他的網(wǎng)址之類的池凄,這時需要實(shí)現(xiàn)一個點(diǎn)擊事件鬼廓,我采用的傳入自定義的block參數(shù),然后在UICollectionViewdidSelectItemAtIndexPath方法中進(jìn)行回調(diào)碎税,和UIPageControlcurrentPage一樣,我們需要注意indexPath.item的值:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:( NSIndexPath *)indexPath {

    if (self.didSelectItemBlock) {
        self.didSelectItemBlock(indexPath.item-1);
    }
}

通過上面的處理伟端,我們就可以實(shí)現(xiàn)無限輪播的功能了匪煌。
但是
只有輪播是不夠的党巾,有的時候我們還需要它能自己進(jìn)行輪播霜医,而不是人為的滑動,在之前基礎(chǔ)上创肥,初始化時我們加一個定時器就可以了
- (void)loadTimer {
self.timer = [NSTimer timerWithTimeInterval:self.timeInterval
target:self
selector:@selector(timerChanged)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer
forMode:NSRunLoopCommonModes];
}
定時器事件方法中這么實(shí)現(xiàn):
- (void)timerChanged {
if (self.currentItem == self.dataArray.count-2) {
NSInteger toItem = 0;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:toItem inSection:0];
[self.carouselCollectionView scrollToItemAtIndexPath:indexPath
atScrollPosition:UICollectionViewScrollPositi onNone
animated:NO];
self.currentItem = toItem;
}

    self.currentItem++;
    
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.currentItem inSection:0];
    [self.carouselCollectionView scrollToItemAtIndexPath:indexPath
                                        atScrollPosition:UICollectionViewScrollPositionLe   ft
                                                animated:YES];
    self.pageControl.currentPage = self.currentItem-1;
}

OK叹侄!廣告可以自動進(jìn)行輪播了昨登,伸手上去滑動兩下,發(fā)現(xiàn)問題了丰辣,手勢和定時器發(fā)生沖突了,我們可以在手勢開始時停止定時器飘哨,手勢停止時重新開啟定時器琐凭,這樣就可以解決沖突了UIScrollViewDelegate中的兩個方法:
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
if (self.timer) {
[self.timer invalidate];
}
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self reloadItemAction];
    
    if (self.timer) {
        [self loadTimer];
    }
}

現(xiàn)在應(yīng)該是完整的廣告無限輪播組件了。對外兩個簡答的接口方法胚吁,一個帶定時器的愁憔,一個不帶定時器的:
- (instancetype)initWithFrame:(CGRect)frame
dataArray:(NSArray *)dataArray
didSelectItemBlock:(void (^)(NSInteger didSelectItem))block;

- (instancetype)initWithFrame:(CGRect)frame
                    dataArray:(NSArray *)dataArray
                 timeInterval:(CGFloat)timeInterval
           didSelectItemBlock:(void (^)(NSInteger didSelectItem))block;

使用的時候,可以這樣:
#define __LOADIMAGE(file, type) [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:file ofType:type]]

- (void)loadCarouselCollectionView {
    // 不使用定時器初始化
    UCCarouselView *carouselView = [[UCCarouselView alloc] initWithFrame:({
        CGRectMake(0.f, 20.f,
                   CGRectGetWidth([UIScreen mainScreen].bounds),
                   200.f);
    }) dataArray:[self loadData] didSelectItemBlock:^(NSInteger didSelectItem) {
        
        NSLog(@"didSelectItem is :%ld", (long)didSelectItem);
        
    }];
    [self.view addSubview:carouselView];
    
    // 使用定時器初始化
//    UCCarouselView *carouselView = [[UCCarouselView alloc] initWithFrame:({
//        CGRectMake(0.f, 20.f,
//                   CGRectGetWidth([UIScreen mainScreen].bounds),
//                   200.f);
//    }) dataArray:[self loadData] timeInterval:2.f didSelectItemBlock:^(NSInteger    didSelectItem) {
//        
//        NSLog(@"didSelectItem is :%ld", (long)didSelectItem);
//    
//    }];
//    [self.view addSubview:carouselView];
}

// Demo 數(shù)據(jù)
- (NSArray *)loadData {
    NSArray *array = @[__LOADIMAGE(@"dota2_0", @"jpg"),
                       __LOADIMAGE(@"dota2_1", @"jpg"),
                       __LOADIMAGE(@"dota2_2", @"jpg"),
                       __LOADIMAGE(@"dota2_3", @"jpg")];
    return array;
}

歡迎各位iOS Developer踴躍指出Bug半抱、意見思犁!希望本文能夠?qū)δ兴鶐椭?br> 完整的Demo源碼看這里进肯!如果感覺還可以來個star也是不錯的江掩!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乘瓤,一起剝皮案震驚了整個濱河市策泣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌统抬,老刑警劉巖危队,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異金麸,居然都是意外死亡簿盅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門棚瘟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喜最,“玉大人,你說我怎么就攤上這事返顺。” “怎么了振乏?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵秉扑,是天一觀的道長。 經(jīng)常有香客問我舟陆,道長,這世上最難降的妖魔是什么秦躯? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任踱承,我火速辦了婚禮哨免,結(jié)果婚禮上昙沦,老公的妹妹穿的比我還像新娘琢唾。我一直安慰自己盾饮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布芍碧。 她就那樣靜靜地躺著号俐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪踪危。 梳的紋絲不亂的頭發(fā)上猪落,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音蓝仲,去河邊找鬼官疲。 笑死,一個胖子當(dāng)著我的面吹牛途凫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播果元,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼犀盟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了倡怎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤豹障,失蹤者是張志新(化名)和其女友劉穎焦匈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缓熟,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摔笤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年吕世,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片命辖。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡尔艇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出终娃,到底是詐尸還是另有隱情,我是刑警寧澤余佛,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布窍荧,位于F島的核電站,受9級特大地震影響搅荞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜痢甘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一茉贡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧放椰,春花似錦、人聲如沸砾医。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽错邦。三九已至,卻和暖如春撬呢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毛仪。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工晨另, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人借尿。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓路翻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茂契。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

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