ios 知识点Xcode8.0 iOS10权限设置 新特性iOS分享的demo

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

2016-09-29  本文已影响2425人  Rasping

之前写的项目,几乎每一个都会用到循环轮播图,于是很早之前就自己写了一个,并一直在使用。在使用过程中也会将出现的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,同时也欢迎大家提出你的想法或意见,我们一起相互学习,共同进步!!!

上一篇下一篇

猜你喜欢

热点阅读