三张 imageView实现轮播图无限轮播

github地址:https://github.com/leshengping/SPCarouselScrollView

#import "SPCarouselScrollView.h"
#import "UIImageView+WebCache.h"

typedef NS_ENUM(NSInteger, SPCarouseImagesDataStyle){
    SPCarouseImagesDataInLocal,//本地图片标记
    SPCarouseImagesDataInURL   //URL图片标记
};

// width和height依赖于传进来的frame
#define  kWidth  self.bounds.size.width
#define  kHeight self.bounds.size.height

@interface SPCarouselScrollView () <UIScrollViewDelegate>

@property(strong, nonatomic) UIScrollView *scrollView;
@property(strong, nonatomic) UIPageControl *pageControl;

// 存放本地图片名字的数组
@property(strong, nonatomic) NSArray<NSString *> *localImages;
//存放图片数组的网址
@property(strong, nonatomic) NSArray<NSString *> *urlImages;


// 前一个视图,当前视图,下一个视图
@property(strong, nonatomic) UIImageView *lastImgView;
@property(strong, nonatomic) UIImageView *currentImgView;
@property(strong, nonatomic) UIImageView *nextImgView;

// 图片来源(本地或URL)
@property(nonatomic) SPCarouseImagesDataStyle carouseImagesStyle;

@property(strong, nonatomic) NSTimer *timer;

// kCount = array.count,图片数组个数
@property(assign, nonatomic) NSInteger kCount;

// 记录nextImageView的下标 默认从1开始
@property(assign, nonatomic) NSInteger nextPhotoIndex;
// 记录lastImageView的下标 默认从 _kCount - 1 开始
@property(assign, nonatomic) NSInteger lastPhotoIndex;

@property(strong, nonatomic) UILabel *label;

@end

@implementation SPCarouselScrollView

#pragma mark - 初始化方法
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    _duration = 2;
    _autoScroll = YES;
    _pageControlColor = [UIColor grayColor];
    _currentPageControlColor = [UIColor whiteColor];
}

#pragma mark - 类方法
// 如果是本地图片调用此方法
+(SPCarouselScrollView *)carouselScrollViewWithFrame:(CGRect)frame localImages:(NSArray<NSString *> *)localImages{
    SPCarouselScrollView *carouseScroll =[[SPCarouselScrollView alloc]initWithFrame:frame];
    // 调用set方法
    carouseScroll.localImages = localImages;
    return carouseScroll;
}

// 如果是网络图片调用此方法
+(SPCarouselScrollView *)carouselScrollViewWithFrame:(CGRect)frame urlImages:(NSArray<NSString *> *)urlImages{
    SPCarouselScrollView *carouseScroll = [[SPCarouselScrollView alloc]initWithFrame:frame];
    // 调用set方法
    carouseScroll.urlImages = urlImages;
    return carouseScroll;
}

#pragma mark - setter方法专区
// 是否自动轮播
- (void)setAutoScroll:(BOOL)autoScroll {
    _autoScroll = autoScroll;

    if (autoScroll) {
        // 开启新的定时器
        [self openTimer];
    } else {
        // 关闭定时器
        [self closeTimer];
    }
}

// 重写duration的set方法,用户可以在外界设置轮播图间隔时间
-(void)setDuration:(NSTimeInterval)duration{
    _duration = duration;
    [self openTimer];
}

// 设置其他小圆点的颜色
- (void)setPageControlColor:(UIColor *)pageControlColor {
    _pageControlColor = pageControlColor;
    _pageControl.pageIndicatorTintColor = pageControlColor;
}

// 设置当前小圆点的颜色
- (void)setCurrentPageControlColor:(UIColor *)currentPageControlColor {
    _currentPageControlColor = currentPageControlColor;
    _pageControl.currentPageIndicatorTintColor = currentPageControlColor;
}

// 设置其他小圆点的图片
- (void)setPageControlImage:(UIImage *)pageControlImage {
    if (pageControlImage == nil) {
        NSLog(@"Error:其余小圆点的图片为nil,请检查");
        return;
    }
    _pageControlImage = pageControlImage;
    [_pageControl setValue:_pageControlImage forKeyPath:@"pageImage"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (_currentPageControlImage == nil) {
            NSLog(@"Error:您只设置了其余小圆点的图片,没有设置当前小圆点的图片");
        }
    });
}

// 设置当前小圆点的图片
- (void)setCurrentPageControlImage:(UIImage *)currentPageControlImage {
    if (currentPageControlImage == nil) {
        NSLog(@"Error:当前小圆点的图片为nil,请检查");
        return;
    }
    _currentPageControlImage = currentPageControlImage;
    [_pageControl setValue:_currentPageControlImage forKeyPath:@"currentPageImage"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (_pageControlImage == nil) {
            NSLog(@"Error:您只设置了当前小圆点的图片,没有设置其他小圆点的图片");
        }
    });
}

// 设置pageControl的位置
- (void)setPageContolAliment:(SPCarouseScrollViewPageContolAliment)pageContolAliment {
    _pageContolAliment = pageContolAliment;

    switch (pageContolAliment) {
        case SPCarouseScrollViewPageContolAlimentCenter:
            // 如果是中间
            _pageControl.frame = CGRectMake(0, 0, kWidth, 30);
            _pageControl.center = CGPointMake(kWidth * 0.5, kHeight - 30 / 2);
            break;
        case SPCarouseScrollViewPageContolAlimentRight:
            // 如果是右边
            _pageControl.frame = CGRectMake(0, 0, kWidth, 30);
            _pageControl.center = CGPointMake(kWidth * 0.5 * 1.5, kHeight - 30 / 2);
            break;
        case SPCarouseScrollViewPageContolAlimentLeft:
            // 如果是左边
            _pageControl.frame = CGRectMake(0, 0, kWidth, 30);
            _pageControl.center = CGPointMake(kWidth * 0.5 * 0.5, kHeight - 30 / 2);
            break;
        default:
            break;
    }
}

// 设置imageView的内容模式
- (void)setImageMode:(SPCarouselScrollViewImageMode)imageMode {
    _imageMode = imageMode;

    switch (imageMode) {
        case SPCarouselScrollViewImageModeScaleToFill:
            _lastImgView.contentMode = UIViewContentModeScaleToFill;
            _currentImgView.contentMode = UIViewContentModeScaleToFill;
            _nextImgView.contentMode = UIViewContentModeScaleToFill;
            break;
        case SPCarouselScrollViewImageModeScaleAspectFit:
            _lastImgView.contentMode = UIViewContentModeScaleAspectFit;
            _currentImgView.contentMode = UIViewContentModeScaleAspectFit;
            _nextImgView.contentMode = UIViewContentModeScaleAspectFit;
            break;
        case SPCarouselScrollViewImageModeScaleAspectFill:
            _lastImgView.contentMode = UIViewContentModeScaleAspectFill;
            _currentImgView.contentMode = UIViewContentModeScaleAspectFill;
            _nextImgView.contentMode = UIViewContentModeScaleAspectFill;
            break;
        case SPCarouselScrollViewImageModeCenter:
            _lastImgView.contentMode = UIViewContentModeCenter;
            _currentImgView.contentMode = UIViewContentModeCenter;
            _nextImgView.contentMode = UIViewContentModeCenter;
            break;
        default:
            break;
    }
}

- (void)setLocalImages:(NSArray<NSString *> *)localImages {
    if (_localImages != localImages) {
        _localImages = nil;
        _localImages = localImages;
    }
    //标记图片来源
    self.carouseImagesStyle = SPCarouseImagesDataInLocal;
    //获取数组个数
    self.kCount = _localImages.count;
    [self drawMyView];

    [self openTimer];
}

- (void)setUrlImages:(NSArray<NSString *> *)urlImages {
    if (_urlImages != urlImages) {
        _urlImages = nil;
        _urlImages = urlImages;
    }
    //标记图片来源
    self.carouseImagesStyle = SPCarouseImagesDataInURL;
    self.kCount = _urlImages.count;
    [self drawMyView];

    [self openTimer];
}

#pragma maek - 其它方法
-(void)drawMyView{
    [self addSubview:self.scrollView];
    [self addSubview:self.pageControl];
    self.nextPhotoIndex = 1;
    self.lastPhotoIndex = _kCount - 1;

    //想在轮播图上添加其他子控件可在这里添加
    /*
     self.label = [[UILabel alloc]initWithFrame:CGRectMake(50, 500,kWidth-100, 30)];
     self.label.backgroundColor = [UIColor redColor];
     [self addSubview:self.label];
     */
}

// 开启定时器
- (void)openTimer {
    // 开启之前一定要先将上一次开启的定时器关闭,否则会跟新的定时器重叠
    [self closeTimer];
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.duration target:self selector:@selector(timerAction) userInfo:self repeats:YES];
    // UITrackingRunLoopMode模式的作用是当用户拖动tableView、collectionView等事件时定时器仍然会处理事件
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    _timer = timer;
}

// 关闭定时器
- (void)closeTimer {
    [_timer invalidate];
    _timer = nil;
}

#pragma mark - 懒加载
-(UIScrollView *)scrollView{
    if (_scrollView == nil) {
        _scrollView = [[UIScrollView alloc]initWithFrame:self.bounds];
        _scrollView.pagingEnabled = YES;
        _scrollView.bounces = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator = NO;
        _scrollView.contentSize = CGSizeMake(kWidth * 3, kHeight);
        //显示中间的图片
        _scrollView.contentOffset = CGPointMake(kWidth, 0);
        _scrollView.delegate = self;
        // 添加最初的三张imageView
        [_scrollView addSubview:self.lastImgView];
        [_scrollView addSubview:self.currentImgView];
        [_scrollView addSubview:self.nextImgView];
    }
    return _scrollView;
}

-(UIPageControl *)pageControl{
    if (_pageControl == nil) {
        _pageControl = [[UIPageControl alloc]initWithFrame:CGRectMake(0, 0, kWidth, 30)];
        _pageControl.center = CGPointMake(kWidth/2.0, kHeight - 30/2.0);
        _pageControl.userInteractionEnabled = NO;
        _pageControl.hidesForSinglePage = YES;
        _pageControl.pageIndicatorTintColor = self.pageControlColor;
        _pageControl.currentPageIndicatorTintColor = self.currentPageControlColor;
        _pageControl.numberOfPages = self.kCount;
        _pageControl.currentPage = 0;
    }
    return _pageControl;
}

-(UIImageView *)lastImgView{
    if (_lastImgView == nil) {
        _lastImgView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, kWidth, kHeight)];
        // 一上来,将上一张图片设置为数组中最后一张图片
        [self setImageView:_lastImgView withSubscript:(_kCount-1)];
    }
    return _lastImgView;
}

-(UIImageView *)currentImgView{
    if (_currentImgView == nil) {
        _currentImgView = [[UIImageView alloc]initWithFrame:CGRectMake(kWidth, 0, kWidth, kHeight)];
        // 一上来,将当前图片设置为数组中第一张图片
        [self setImageView:_currentImgView withSubscript:0];
        // 给当前图片添加手势
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTapActionInImageView:)];
        [_currentImgView addGestureRecognizer:tap];
        _currentImgView.userInteractionEnabled = YES;
    }
    return _currentImgView;
}

-(UIImageView *)nextImgView{
    if (_nextImgView == nil) {
        _nextImgView = [[UIImageView alloc]initWithFrame:CGRectMake(kWidth*2, 0,kWidth , kHeight)];
        // 一上来,将下一张图片设置为数组中第二张图片,如果数组只有一张图片,则上、中、下图片全部是数组中的第一张图片
        [self setImageView:_nextImgView withSubscript:_kCount == 1 ? 0 : 1];
    }
    return _nextImgView;
}

//根据下标设置imgView的image
-(void)setImageView:(UIImageView *)imgView withSubscript:(NSInteger)subcript{
    if (self.carouseImagesStyle == SPCarouseImagesDataInLocal) {
        imgView.image = [UIImage imageNamed:self.localImages[subcript]];
    } else{
        //网络图片设置, 如果要使用占位图请自行修改
        [imgView sd_setImageWithURL:[NSURL URLWithString:self.urlImages[subcript]] placeholderImage:nil];
    }
}

#pragma mark - scrollView代理方法
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    //到第一张图片时   (一上来,当前图片的x值是kWidth)
    if (scrollView.contentOffset.x <= 0) {
        // 这个if语句只有scrollView往右滑时才有会进入
        _nextImgView.image = _currentImgView.image;
        _currentImgView.image = _lastImgView.image;
        // 将轮播图的偏移量设回中间位置
        scrollView.contentOffset = CGPointMake(kWidth, 0);
        _lastImgView.image = nil;
        // 一定要是小于等于,否则数组中只有一张图片时会出错
        if (_lastPhotoIndex <= 0) {
            _lastPhotoIndex = _kCount - 1;
            _nextPhotoIndex = _lastPhotoIndex - (_kCount - 2);
        } else {
            _lastPhotoIndex--;
            if (_nextPhotoIndex == 0) {
                _nextPhotoIndex = _kCount - 1;
            } else {
                _nextPhotoIndex--;
            }
        }
        [self setImageView:_lastImgView withSubscript:_lastPhotoIndex];
    }
    // 到最后一张图片时(最后一张就是轮播图的第三张)
    if (scrollView.contentOffset.x  >= kWidth*2) {
        // 这个if语句只有scrollView往左滑时才会进入
        _lastImgView.image = _currentImgView.image;
        _currentImgView.image = _nextImgView.image;
        // 将轮播图的偏移量设回中间位置
        scrollView.contentOffset = CGPointMake(kWidth, 0);
        _nextImgView.image = nil;
        // 一定要是大于等于,否则数组中只有一张图片时会出错
        if (_nextPhotoIndex >= _kCount - 1 ) {
            _nextPhotoIndex = 0;
            _lastPhotoIndex = _nextPhotoIndex + (_kCount - 2);
        } else{
            _nextPhotoIndex++;
            if (_lastPhotoIndex == _kCount - 1) {
                _lastPhotoIndex = 0;
            } else {
                _lastPhotoIndex++;
            }
        }
        [self setImageView:_nextImgView withSubscript:_nextPhotoIndex];
    }
}

// scrollView结束减速的时候调用
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    //矫正pageCotrol的位置
    _pageControl.currentPage = _nextPhotoIndex - 1;
    if (_nextPhotoIndex - 1 < 0) {
        _pageControl.currentPage = _kCount-1;
    }
    ;
}

// 用户将要拖拽时将定时器关闭
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    // 关闭定时器
    [self closeTimer];
}

// 用户结束拖拽时将定时器开启(在打开自动轮播的前提下)
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (self.autoScroll) {
        [self openTimer];
    }
}

#pragma mark - timer事件
-(void)timerAction{
    // 一上来,轮播图显示的是中间的那张图片,定时器每次触发都让当前图片为轮播图的第三张ImageView的image
    [_scrollView setContentOffset:CGPointMake(2*kWidth, 0) animated:YES];
    _pageControl.currentPage = _nextPhotoIndex;
}

#pragma mark - 手势点击事件
-(void)handleTapActionInImageView:(UITapGestureRecognizer *)tap {
    if (_delegate && [_delegate respondsToSelector:@selector(carouseScrollView:atIndex:)]) {
        // 如果_nextPhotoIndex == 0,那么中间那张图片一定是数组中最后一张,我们要传的就是中间那张图片在数组中的下标
        if (_nextPhotoIndex == 0) {
            [_delegate carouseScrollView:self atIndex:_kCount-1];
        }else{
            [_delegate carouseScrollView:self atIndex:_nextPhotoIndex-1];
        }
    }
}

#pragma mark - 系统方法
-(void)willMoveToSuperview:(UIView *)newSuperview {
    if (!newSuperview) {
        [self closeTimer];
    }
}

-(void)dealloc {
    _scrollView.delegate = nil;
}

@end

 

posted @ 2016-09-12 09:21  S型身材的猪  阅读(380)  评论(0编辑  收藏  举报