利用UICollectionView实现无限循环轮播图

之前写的项目,几乎每一个都会用到循环轮播图,于是很早之前就自己写了一个,并一直在使用。在使用过程中也会将出现的BUG进行修复,还会根据需求添加一些新的功能,这个功能就一直修修改改的用着。今天抽出点时间,对这份代码进行了整理和完善,现在分享出来供大家一起学习。
实现效果,如下图所示:

效果图.png

思路与实现

其实在明白设计思路之后,实现起来就非常简单,所以为这里会先介绍实现的思路。

思路

我们都知道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,同时也欢迎大家提出你的想法或意见,我们一起相互学习,共同进步!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,977评论 4 60
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,259评论 25 707
  • 每个人身边都有一个或者几个特别喜欢写作的朋友,和他们聊天相处,你总会不知觉的成为他们日后笔下的素材,也许你的想法和...
    江南少城主阅读 947评论 23 23
  • 常量与变量使用let来声明常量,使用var来声明变量。声明的同时赋值的话,编译器会自动推断类型。值永远不会被隐式转...
    莫_名阅读 433评论 0 1
  • 昨夜的风是你吗 染白了门前的梨花 用思念做养料 在梦中开出繁华 把春水搅乱 倒映的是你的脸颊 有种纷乱的情绪 叫做...
    甘棠遗爱阅读 240评论 4 3