iOS基于UICollectionView实现一个无限轮播

欢迎下载:项目地址

网上这么多造好的轮子,为啥自己要去造个轮子?

其实自己在造轮子之前也看了网上的一些实现方案:

1. UIScrollView与N + 2个UIImageView
2. UIScrollView与1 + 2个UIImageView
3. UICollectionView与N乘以一个很大的数字,然后初始化的时候滚到中间位置

这些方案多多少少都会有一些问题:

方案一:如果有大量的图片需要显示就会造成内存的暴增,严重影响APP的性能
方案二:虽然不会造成内存暴增,但是实现起来也是比较繁琐,需要反复的调整各个UIImageView的位置,计算现在显示的是哪一个图片,代码体积较为庞大
方案三:主要的问题是实现不够优雅

下面说说我的实现

先说说为啥选用UICollectionView,它最大的一个优点就是cell的复用机制,这个机制保证了不论我需要显示多少张图片,它在内存中实际就创建两个cell
原理:返回N + 2个cell,indexPath.row == 0的cell展示最后一张图片,indexPath.row == N + 1的cell展示第一张图片,这样就形成了一个循环,当滚动到indexPath.row == 0时偏移到indexPath.row == N的cell;当滚动到indexPath.row == N + 1时偏移到indexPath.row == 1的cell
完整实现代码如下:

//
//  DABannerView.m
//  DABannerView
//
//  Created by linfeng wang on 2019/7/22.
//  Copyright © 2019 linfeng wang. All rights reserved.
//

#import "DABannerView.h"
#import "DABannerCollectionViewCell.h"
#import "DABannerFlowLayout.h"

#define random(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)/255.0]
#define randomColor random(arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256))

@interface DABannerView ()

@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) NSTimer *timer;

@end

@implementation DABannerView {
    NSArray *_colorArr;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _colorArr = @[[UIColor redColor],[UIColor greenColor],[UIColor blueColor],[UIColor yellowColor]];
        [self addSubview:self.collectionView];
        [self addSubview:self.pageControl];
    }
    return self;
}

#pragma mark -- action
- (void)timerAction:(NSTimer *)timer {
    NSIndexPath *indexPath = [self.collectionView indexPathsForVisibleItems].lastObject;
    NSIndexPath *nextPath = [NSIndexPath indexPathForItem:(indexPath.item + 1)%(self.urlStringForImageArr.count+2) inSection:indexPath.section];
    [self.collectionView scrollToItemAtIndexPath:nextPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}

#pragma mark -- collectionView delegate and datasource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.urlStringForImageArr.count + 2;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    DABannerCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"DABannerCollectionViewCell" forIndexPath:indexPath];
    if (indexPath.row == 0) {
        cell.backgroundColor = _colorArr.lastObject;
    }else if (indexPath.row ==_urlStringForImageArr.count + 1) {
        cell.backgroundColor = _colorArr.firstObject;
    }else {
        cell.backgroundColor = _colorArr[indexPath.row - 1];
    }
    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    
}

#pragma mark -- scrollview delegate
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView{
    CGFloat offsetX = scrollView.contentOffset.x;
    NSInteger page = offsetX / self.bounds.size.width;
    
    NSInteger itemsCount = [self.collectionView numberOfItemsInSection:0];
    if (page == 0) { // 第一页
        [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.urlStringForImageArr.count inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
    } else if (page == itemsCount - 1) { // 最后一页
        [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
    }
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView{
    // 手动调用减速完成的方法
    [self scrollViewDidEndDecelerating:self.collectionView];
}

- (void)scrollViewDidScroll:(UIScrollView*)scrollView{
    CGFloat offsetX = scrollView.contentOffset.x;
    CGFloat page = offsetX / self.bounds.size.width;
    NSInteger itemsCount = [self.collectionView numberOfItemsInSection:0];
    if (page == 0) { // 第一页
        self.pageControl.currentPage = self.urlStringForImageArr.count - 1;
    } else if (page == itemsCount - 1) { // 最后一页
        self.pageControl.currentPage = 0;
    }else {
        self.pageControl.currentPage = page - 1;
    }
}

#pragma mark -- setter and getter
- (UICollectionView *)collectionView {
    if (!_collectionView) {
        DABannerFlowLayout *layout = [[DABannerFlowLayout alloc] init];
        _collectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:layout];
        _collectionView.showsHorizontalScrollIndicator = NO;
        _collectionView.bounces = NO;
        _collectionView.pagingEnabled = YES;
        _collectionView.showsVerticalScrollIndicator = NO;
        _collectionView.showsHorizontalScrollIndicator = NO;
        _collectionView.dataSource = self;
        _collectionView.delegate = self;
        [_collectionView registerNib:[UINib nibWithNibName:@"DABannerCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"DABannerCollectionViewCell"];
    }
    return _collectionView;
}

- (UIPageControl *)pageControl {
    if (!_pageControl) {
        CGFloat width = 120;
        CGFloat height = 20;
        CGFloat pointX = ([UIScreen mainScreen].bounds.size.width - width) / 2;
        CGFloat pointY = self.bounds.size.height - height;
        _pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(pointX, pointY, width, height)];
        _pageControl.numberOfPages = self.urlStringForImageArr.count;
        _pageControl.userInteractionEnabled = NO;
        _pageControl.pageIndicatorTintColor = [UIColor lightTextColor];
        _pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];
    }
    return _pageControl;
}

- (void)setUrlStringForImageArr:(NSArray *)urlStringForImageArr {
    _urlStringForImageArr = urlStringForImageArr;
    self.pageControl.currentPage = 0;
    self.pageControl.numberOfPages = urlStringForImageArr.count;
    [self.collectionView reloadData];
    //滚动到中间位置
    NSIndexPath* indexPath = [NSIndexPath indexPathForItem:1 inSection:0];
    [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
    _timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
    _timer.fireDate = [NSDate dateWithTimeIntervalSinceNow:2];
}

@end

你可能感兴趣的:(iOS基于UICollectionView实现一个无限轮播)