前言
廣告無限輪播跌穗,隨處可見的功能,也有很多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)該是這個樣子的:
對于初始數(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)該是這個樣子的:
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的英支,所以我們在操作UIPageControl
的currentPage
時哮伟,需要注意這一點(diǎn)。
還有我們會需要點(diǎn)擊某一張圖片進(jìn)行詳情的查看或者跳轉(zhuǎn)到一個其他的網(wǎng)址之類的池凄,這時需要實(shí)現(xiàn)一個點(diǎn)擊事件鬼廓,我采用的傳入自定義的block參數(shù),然后在UICollectionView
的didSelectItemAtIndexPath
方法中進(jìn)行回調(diào)碎税,和UIPageControl
的currentPage
一樣,我們需要注意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也是不錯的江掩!