之前寫(xiě)的項(xiàng)目堵腹,幾乎每一個(gè)都會(huì)用到循環(huán)輪播圖炸站,于是很早之前就自己寫(xiě)了一個(gè),并一直在使用疚顷。在使用過(guò)程中也會(huì)將出現(xiàn)的BUG進(jìn)行修復(fù)旱易,還會(huì)根據(jù)需求添加一些新的功能,這個(gè)功能就一直修修改改的用著腿堤。今天抽出點(diǎn)時(shí)間咒唆,對(duì)這份代碼進(jìn)行了整理和完善,現(xiàn)在分享出來(lái)供大家一起學(xué)習(xí)释液。
實(shí)現(xiàn)效果,如下圖所示:
思路與實(shí)現(xiàn)
其實(shí)在明白設(shè)計(jì)思路之后装处,實(shí)現(xiàn)起來(lái)就非常簡(jiǎn)單误债,所以為這里會(huì)先介紹實(shí)現(xiàn)的思路。
思路
我們都知道UICollectionView是系統(tǒng)提供的一個(gè)用于展示各種圖片之類(lèi)的展示的控件妄迁,用UICollectionView展示一組圖片使其可以左右滑動(dòng)寝蹈,包括讓其自動(dòng)滾動(dòng),這些都非常簡(jiǎn)單登淘。但如何讓其實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)呢箫老?
其實(shí)原理非常簡(jiǎn)單,就是讓UICollectionView的數(shù)據(jù)源數(shù)組里面多放幾組將要展示的圖片黔州,然后讓UICollectionView滾動(dòng)到中間位置就OK了耍鬓。這里我們需要清楚:數(shù)組中多了那么多圖片阔籽,會(huì)不會(huì)導(dǎo)致內(nèi)存增加呢?其實(shí)牲蜀,這個(gè)完全不用擔(dān)心笆制,因?yàn)閿?shù)組中存放的是圖片的內(nèi)存地址,所以圖片的增加不會(huì)對(duì)內(nèi)存有多大的影響涣达。明白了思路之后在辆,就直接上代碼。
實(shí)現(xiàn)
加載本地圖片
在設(shè)置好輪播圖的位置等一些基本屬性之后度苔,還需要給輪播圖傳一個(gè)圖片數(shù)組addLocalImages:匆篓,具體實(shí)現(xiàn)如下:
- (void)addLocalImages:(NSArray<NSString *> *)images
{
[ImagesPlayer checkElementOfImages:images];
[self.dataArray removeAllObjects];
[self.dataArray addObjectsFromArray:images];
_images = [NSArray arrayWithArray:self.dataArray];
//刷新pageControl
self.indicatorView.numberOfPages = images.count;
[self.indicatorView updateCurrentPageDisplay];
//在Updates里執(zhí)行完更新操作后再執(zhí)行completion回調(diào)
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadData];
} completion:^(BOOL finished) {
//刷新完成讓collectionView滾動(dòng)到中間位置
NSInteger center = ceilf([self.collectionView numberOfItemsInSection:0] * 0.5);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:center inSection:0];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
self.previousOffsetX = self.collectionView.contentOffset.x;
//開(kāi)啟定時(shí)器
[self removeTimer];
[self addTimer];
}];
}
這里需要注意的是:每次有新的圖片數(shù)組添加進(jìn)來(lái)時(shí),都需要對(duì)UICollectionView進(jìn)行reloadData才能展示新的圖片寇窑,然后再讓UICollectionView滾動(dòng)到中間位置鸦概,但必須要等到reloadData完成才能滾動(dòng)。所以這里需要用到performBatchUpdates:completion:這個(gè)方法疗认,在updates代碼塊中執(zhí)行對(duì)UICollectionView的更新操作完残,等更新操作完成再調(diào)用completion代碼塊中的代碼。
加載網(wǎng)絡(luò)圖片
因?yàn)樵陧?xiàng)目中輪播圖片是從后臺(tái)獲取的横漏,所以還需要加載網(wǎng)絡(luò)圖片addNetWorkImages:placeholder:谨设。這個(gè)方法與上面加載本地圖片方法相比,就是多了一個(gè)占位圖缎浇,然后利用圖片地址請(qǐng)求網(wǎng)絡(luò)圖片扎拣,請(qǐng)求到圖片后還需做本地緩存,具體實(shí)現(xiàn)如下:
- (void)setImageWithURL:(NSString *)url placeholderImage:(UIImage *)placeholder
{
NSString *fileDir = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"imagesCache"];
NSFileManager *fm = [NSFileManager defaultManager];
[fm createDirectoryAtPath:fileDir withIntermediateDirectories:YES attributes:nil error:nil];
NSString *fileName = [fileDir stringByAppendingPathComponent:[self md5:url]];//MD5加密圖片名全路徑
UIImage *image = [UIImage imageWithContentsOfFile:fileName];
if (image) {
self.image = image;
}else {
self.image = placeholder;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *path = [NSURL URLWithString:url];
NSData *data = [NSData dataWithContentsOfURL:path];
dispatch_async(dispatch_get_main_queue(), ^{
self.image = [UIImage imageWithData:data];
});
[data writeToFile:fileName atomically:YES];
});
}
}
緩存思路:先從本地取素跺,如果取不到就從網(wǎng)絡(luò)請(qǐng)求二蓝,請(qǐng)求到后存在本地。
因?yàn)樯婕暗奖镜鼐彺嬷秆幔陀星謇砭彺娴男枨罂蓿琧alculateCacheImagesMemory計(jì)算本地緩存的圖片大小,removeCacheMemory清空本地緩存踩验,代碼實(shí)現(xiàn)比較簡(jiǎn)單鸥诽,具體可以在Demo中查看。
分頁(yè)指示器
在項(xiàng)目中因?yàn)轫?xiàng)目UI的要求箕憾,經(jīng)常會(huì)遇到各種不同的分頁(yè)指示器牡借。功能中默認(rèn)的是系統(tǒng)自帶的UIPageControl,也提供了自定義分類(lèi)指示器的接口,使用也很簡(jiǎn)單袭异,只需要先遵守ImagesPlayerIndictorPattern協(xié)議钠龙,并實(shí)現(xiàn)該協(xié)議的方法就行了,具體代碼如下:
- (UIView *)indicatorViewInImagesPlayer:(ImagesPlayer *)imagesPlayer
{
CGFloat margin = 5.0;
UIView *view = [[UIView alloc] init];
CGFloat w = 50;
CGFloat h = 20;
CGFloat x = CGRectGetWidth(imagesPlayer.frame) - w - margin;
CGFloat y = CGRectGetHeight(imagesPlayer.frame) - h - margin;
view.frame = CGRectMake(x, y, w, h);
view.backgroundColor = [UIColor blackColor];
view.alpha = 0.5;
view.clipsToBounds = YES;
view.layer.cornerRadius = 5.0;
UILabel *lable = [[UILabel alloc] initWithFrame:view.bounds];
lable.textAlignment = NSTextAlignmentCenter;
lable.textColor = [UIColor whiteColor];
self.lable = lable;
[view addSubview:lable];
return view;
}
返回自定義的分頁(yè)指示器的樣式
- (void)imagesPlayer:(ImagesPlayer *)imagesPlayer didChangedIndex:(NSInteger)index count:(NSInteger)count
{
self.lable.text = [NSString stringWithFormat:@"%ld/%ld", index, count];
}
更新分頁(yè)指示器的顯示
事件處理
這里就對(duì)輪播圖的點(diǎn)擊事件進(jìn)行處理,這里可以通過(guò)代理監(jiān)聽(tīng)或是設(shè)置代碼回調(diào)碴里。
代碼監(jiān)聽(tīng):遵守ImagesPlayerDelegae代理沈矿,實(shí)現(xiàn)imagesPlayer:didSelectImageAtIndex:方法。
代碼回調(diào):通過(guò)imageTapAction:接口設(shè)置回調(diào)代碼塊并闲。
最后需要說(shuō)明的是细睡,在當(dāng)前控制器的viewDidDisappear中要移除定時(shí)器removeTimer,防止沒(méi)必要的CPU消耗帝火。
至此溜徙,整個(gè)輪播圖的基本功能都已實(shí)現(xiàn),大家可以下載Demo犀填,同時(shí)也歡迎大家提出你的想法或意見(jiàn)蠢壹,我們一起相互學(xué)習(xí),共同進(jìn)步>叛病M济场!