利用UICollectionView实现无限循环轮播图
之前写的项目,几乎每一个都会用到循环轮播图,于是很早之前就自己写了一个,并一直在使用。在使用过程中也会将出现的BUG进行修复,还会根据需求添加一些新的功能,这个功能就一直修修改改的用着。今天抽出点时间,对这份代码进行了整理和完善,现在分享出来供大家一起学习。
实现效果,如下图所示:
思路与实现
其实在明白设计思路之后,实现起来就非常简单,所以为这里会先介绍实现的思路。
思路
我们都知道UICollectionView是系统提供的一个用于展示各种图片之类的展示的控件,用UICollectionView展示一组图片使其可以左右滑动,包括让其自动滚动,这些都非常简单。但如何让其实现无限循环滚动呢?
其实原理非常简单,就是让UICollectionView的数据源数组里面多放几组将要展示的图片,然后让UICollectionView滚动到中间位置就OK了。这里我们需要清楚:数组中多了那么多图片,会不会导致内存增加呢?其实,这个完全不用担心,因为数组中存放的是图片的内存地址,所以图片的增加不会对内存有多大的影响。明白了思路之后,就直接上代码。
实现
加载本地图片
在设置好轮播图的位置等一些基本属性之后,还需要给轮播图传一个图片数组addLocalImages:,具体实现如下:
- (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里执行完更新操作后再执行completion回调
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadData];
} completion:^(BOOL finished) {
//刷新完成让collectionView滚动到中间位置
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;
//开启定时器
[self removeTimer];
[self addTimer];
}];
}
这里需要注意的是:每次有新的图片数组添加进来时,都需要对UICollectionView进行reloadData才能展示新的图片,然后再让UICollectionView滚动到中间位置,但必须要等到reloadData完成才能滚动。所以这里需要用到performBatchUpdates:completion:这个方法,在updates代码块中执行对UICollectionView的更新操作,等更新操作完成再调用completion代码块中的代码。
加载网络图片
因为在项目中轮播图片是从后台获取的,所以还需要加载网络图片addNetWorkImages:placeholder:。这个方法与上面加载本地图片方法相比,就是多了一个占位图,然后利用图片地址请求网络图片,请求到图片后还需做本地缓存,具体实现如下:
- (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];
});
}
}
缓存思路:先从本地取,如果取不到就从网络请求,请求到后存在本地。
因为涉及到本地缓存,就有清理缓存的需求,calculateCacheImagesMemory计算本地缓存的图片大小,removeCacheMemory清空本地缓存,代码实现比较简单,具体可以在Demo中查看。
分页指示器
在项目中因为项目UI的要求,经常会遇到各种不同的分页指示器。功能中默认的是系统自带的UIPageControl,也提供了自定义分类指示器的接口,使用也很简单,只需要先遵守ImagesPlayerIndictorPattern协议,并实现该协议的方法就行了,具体代码如下:
- (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;
}
返回自定义的分页指示器的样式
- (void)imagesPlayer:(ImagesPlayer *)imagesPlayer didChangedIndex:(NSInteger)index count:(NSInteger)count
{
self.lable.text = [NSString stringWithFormat:@"%ld/%ld", index, count];
}
更新分页指示器的显示
事件处理
这里就对轮播图的点击事件进行处理,这里可以通过代理监听或是设置代码回调。
代码监听:遵守ImagesPlayerDelegae代理,实现imagesPlayer:didSelectImageAtIndex:方法。
代码回调:通过imageTapAction:接口设置回调代码块。
最后需要说明的是,在当前控制器的viewDidDisappear中要移除定时器removeTimer,防止没必要的CPU消耗。
至此,整个轮播图的基本功能都已实现,大家可以下载Demo,同时也欢迎大家提出你的想法或意见,我们一起相互学习,共同进步!!!