iOS 图片轮播

前言

在手机APP的开中,很多首页需要添加广告位,进行广告图片的轮播。我第一次遇到这种需求是在2014年,当时网上类似的demo也有,但是感觉不好用,于是自己奋笔疾弛写了一个。那时候出道不久,很多东西都是用野路子搞出来的,没有用SDWebImage去缓存网络图片。由于网络请求图片需要一段时间,我先在广告轮播上显示一张本地的默认图片,同时开启一个子线程去下载网络图片,等下载好了然后再刷新显示网络图片。这样做虽然实现了效果,但是走了弯路而且体验不好。在2015年,某个项目中又遇到了广告图片轮播这个需求,很显然不能再用之前那种思路去实现了。当时,网上这种类似的demo很多了,为了赶项目进度我就从网上下载了一个,现在到了2016年又遇上了。在开发中有一种莫名的感觉,某些东西你没搞懂,当时应付过去了,但是以后还老是会来找你。所以,我还是自己好好写一个,以后就再也不用怕它了。虽然现在iOS开发很成熟了,很多东西网上有demo,但是我觉得技术这东西你自己掌握了,才是你的。

实现思路

图片轮播其实就是几张图片在进行切换,我们可以想象一下,用scrollView去实现。在scrollView上创建三个imageView, 分别用于显示上一张图片,当前显示图片,下一张图片。当我们向左滑动时,显示上一张图片;向右滑动时,显示下一张图片。下面我们一步步来实现。

iOS 图片轮播_第1张图片

实现步骤

一、实现左右滑动切换图片

1、图片轮播一般是显示网络图片,所以我们需要用到图片缓存,请先添加SDWebImage。

2、新建一个类,继承UIView,命名为:HSImageBanner。在HSImageBanner.m文件中添加图片缓存SDWebImage的头文件:

#import "UIImageView+WebCache.h"

3、初始化一个ScrollView,设置contentSize为其宽度的3倍,然后再ScrollView上添加三个ImageView,代码如下:

#define IMAGEBANNER_WIDTH   _scrollView.bounds.size.width
#define IMAGEBANNER_HEIGHT  _scrollView.bounds.size.height


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

@interface HSImageBanner ()<UIScrollViewDelegate>

@property (nonatomic, strong) UIImageView *imageView1; //左
@property (nonatomic, strong) UIImageView *imageView2; //中
@property (nonatomic, strong) UIImageView *imageView3; //右
@property (nonatomic, strong) UIScrollView  *scrollView;
@end

@implementation HSImageBanner

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _scrollView = [[UIScrollView alloc] initWithFrame:frame];
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator   = NO;
        _scrollView.delegate = self;
        _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);
        _scrollView.contentSize = CGSizeMake(IMAGEBANNER_WIDTH*3, IMAGEBANNER_HEIGHT);
        _scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
        _scrollView.pagingEnabled = YES;
        _scrollView.bounces = NO;
        [self addSubview:_scrollView];

        _imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH*0, 0, IMAGEBANNER_WIDTH, IMAGEBANNER_HEIGHT)];
        _imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH*1, 0, IMAGEBANNER_WIDTH, IMAGEBANNER_HEIGHT)];
        _imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH*2, 0, IMAGEBANNER_WIDTH, IMAGEBANNER_HEIGHT)];

        [_scrollView addSubview:_imageView1];
        [_scrollView addSubview:_imageView2];
        [_scrollView addSubview:_imageView3];
    }
    return self;
}

4、设置轮播的图片

定义一个可变数组用于存放图片,定义三个整形变量,用于记录显示图片数组中的图片下标。

@interface HSImageBanner ()<UIScrollViewDelegate>
{
    NSInteger index1,index2,index3;

    NSMutableArray *imageUrls;
}

index1、index2、index3就好比三个指针,index1指向图片数组中的最后一张图片,index2指向图片数组中的第一张图片,index2指向图片数组中的最二张图片,与imageView1,imageView2,imageView3相对应。

//设置图片轮播的图片
- (void)setImageBannerImages:(NSArray *)images
{
    index1 = images.count-1; //指向最后一张图片
    index2 = 0;              //指向最第一张图片
    index3 = 1;              //指向最第二张图片

    //如果只有一张图片,则禁止滑动
    if (images.count == 1) {
        _scrollView.scrollEnabled = NO;
        index3 = 0;
    }

    imageUrls = [[NSMutableArray alloc] init];

    for (NSString *string in images) {
        [imageUrls addObject:[NSURL URLWithString:string]];
    }

    [_imageView1 sd_setImageWithURL:imageUrls[index1] placeholderImage:_placeHolderImage];
    [_imageView2 sd_setImageWithURL:imageUrls[index2] placeholderImage:_placeHolderImage];
    [_imageView3 sd_setImageWithURL:imageUrls[index3] placeholderImage:_placeHolderImage];
}

5、实现图片左右切换

(1) 向左滑其实就是把指针往图片数组中左移一格,所有的index指针都减1。往左最多只能够移动到图片数组下标为0的位置,当index的值为-1的时候,就说明移动到了数组的最左端,此时应该把index指向数组的最右端。

        index1 = index1-1;
        index2 = index2-1;
        index3 = index3-1;

        if (index1 == -1) {
            index1 = imageUrls.count-1;
        }
        if (index2 == -1) {
            index2 = imageUrls.count-1;
        }
        if (index3 == -1) {
            index3 = imageUrls.count-1;
        }

同理,向右滑其实就是把指针往图片数组中右移一格,所有的index指针都加1。往右最多只能够移动到图片数组下标为imageUrls.cout的位置,当index的值为imageUrls.cout的时候,就说明移动到了数组的最右端,此时应该把index指向数组的最左端。这样就可以实现循环切换了。

        index1 = index1+1;
        index2 = index2+1;
        index3 = index3+1;

        if (index1 == imageUrls.count) {
            index1 = 0;
        }
        if (index2 == imageUrls.count) {
            index2 = 0;
        }
        if (index3 == imageUrls.count) {
            index3 = 0;
        }

(2) 那么如何判断用户是向左滑还是往右滑呢?请看我上面的示意图,设置了两个关键点,_scrollView.contentOffset.x=1 倍宽度和_scrollView.contentOffset.x=2倍宽度。当用户往左滑,contentOffset.x就会停留到_scrollView.contentOffset.x=0的位置;当用户往右滑,contentOffset.x就会停留到_scrollView.contentOffset.x=2倍宽度的位置。

 if (_scrollView.contentOffset.x == 0) { //左滑

  } else if (_scrollView.contentOffset.x == IMAGEBANNER_WIDTH*2) { //右滑

  } else return;

(3)最终,我们需要固定ScrollView的contentOffSet的位置,其实我们切换的只是图片,contentOffSet的位置不能变。变的是图片,而不是位置,这里需要转换一下思维。

    _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);

把这三步的代码综合一下,如下:

#pragma mark - UIScrollView Delegate

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if (_scrollView.contentOffset.x == 0) { //左滑

        index1 = index1-1;
        index2 = index2-1;
        index3 = index3-1;

        if (index1 == -1) {
            index1 = imageUrls.count-1;
        }
        if (index2 == -1) {
            index2 = imageUrls.count-1;
        }
        if (index3 == -1) {
            index3 = imageUrls.count-1;
        }

    } else if (_scrollView.contentOffset.x == IMAGEBANNER_WIDTH*2) { //右滑

        index1 = index1+1;
        index2 = index2+1;
        index3 = index3+1;

        if (index1 == imageUrls.count) {
            index1 = 0;
        }
        if (index2 == imageUrls.count) {
            index2 = 0;
        }
        if (index3 == imageUrls.count) {
            index3 = 0;
        }
    } else return;

    [_imageView1 sd_setImageWithURL:imageUrls[index1] placeholderImage:_placeHolderImage];
    [_imageView2 sd_setImageWithURL:imageUrls[index2] placeholderImage:_placeHolderImage];
    [_imageView3 sd_setImageWithURL:imageUrls[index3] placeholderImage:_placeHolderImage];

    _pageControl.currentPage = index2;
    _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);

}

6、我们来测试一下效果,创建一个对外的调用方法。如果网络比较慢,我们可以设置一张本地的默认图片。

+ (HSImageBanner *)initHSImageBannerWithFrame:(CGRect)frame images:(NSArray *)images placehoderImage:(UIImage *)image
{
    if (images.count == 0) return nil; //没有图片时,直接return
    HSImageBanner *imageBanner = [[HSImageBanner alloc] initWithFrame:frame];
    [imageBanner setImageBannerImages:images];
    [imageBanner setPlaceHolderImage:image];
    return imageBanner;
}

记得在头文件中声明此方法:

#import 

@interface HSImageBanner : UIView

+ (HSImageBanner *)initHSImageBannerWithFrame:(CGRect)frame images:(NSArray *)images placehoderImage:(UIImage *)image;

@end

在ViewController.m添加代码:

#import "ViewController.h"
#import "HSImageBanner.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSArray *arrImage = @[@"http://ofmw9dt1n.bkt.clouddn.com/image_banner_1.jpg",
                          @"http://ofmw9dt1n.bkt.clouddn.com/image_banner_2.jpg",
                          @"http://ofmw9dt1n.bkt.clouddn.com/image_banner_3.jpg"];

    HSImageBanner *imageBanner = [HSImageBanner initHSImageBannerWithFrame:CGRectMake(0, 20, self.view.frame.size.width, 300)
                                                                    images:arrImage
                                                           placehoderImage:[UIImage imageNamed:@"avator"]];
    [self.view addSubview:imageBanner];

}

@end

效果图:

iOS 图片轮播_第2张图片

二、添加定时器,实现自动滚动

1、添加PageControl

定义一个pageControl

@property (nonatomic, strong) UIPageControl *pageControl;

这个比较容易,没有什么可讲解的,直接上代码:

//设置图片轮播的PageControl
- (void)setImageBannerPageControl
{
    _pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH-imageUrls.count*10,  CGRectGetMaxY(_scrollView.frame)-20, 0, 20)];
    _pageControl.numberOfPages = imageUrls.count;
    _pageControl.backgroundColor = [UIColor redColor];
    _pageControl.currentPage = 0;
    [self addSubview:_pageControl];
}

2、添加定时器,自动轮播图片

(1) 在- (id)initWithFrame:(CGRect)frame方法中添加定时器:

 [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:@selector(setImageBannerAutoMove) userInfo:nil repeats:YES];

(2) 图片自动轮播其实就是相当于用户向右滑,代码如下:

//设置图片轮播自动滚动
- (void)setImageBannerAutoMove
{
    index1 = index1+1;
    index2 = index2+1;
    index3 = index3+1;

    if (index1 == imageUrls.count) {
        index1 = 0;
    }
    if (index2 == imageUrls.count) {
        index2 = 0;
    }
    if (index3 == imageUrls.count) {
        index3 = 0;
    }

    [_imageView1 sd_setImageWithURL:imageUrls[index1] placeholderImage:_placeHolderImage];
    [_imageView2 sd_setImageWithURL:imageUrls[index2] placeholderImage:_placeHolderImage];
    [_imageView3 sd_setImageWithURL:imageUrls[index3] placeholderImage:_placeHolderImage];

    _pageControl.currentPage = index2;
    _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);
}

(3) 在initHSImageBannerWithFrame方法中添加

    [imageBanner setImageBannerPageControl];

三、完整的代码

#define IMAGEBANNER_WIDTH   _scrollView.bounds.size.width
#define IMAGEBANNER_HEIGHT  _scrollView.bounds.size.height


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

@interface HSImageBanner ()<UIScrollViewDelegate>
{
    NSInteger index1,index2,index3;

    NSMutableArray *imageUrls;
}

@property (nonatomic, strong) UIImageView *imageView1; //左
@property (nonatomic, strong) UIImageView *imageView2; //中
@property (nonatomic, strong) UIImageView *imageView3; //右

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




@end

@implementation HSImageBanner

+ (HSImageBanner *)initHSImageBannerWithFrame:(CGRect)frame images:(NSArray *)images placehoderImage:(UIImage *)image
{
    if (images.count == 0) return nil; //没有图片时,直接return
    HSImageBanner *imageBanner = [[HSImageBanner alloc] initWithFrame:frame];
    [imageBanner setImageBannerImages:images];
    [imageBanner setPlaceHolderImage:image];
    [imageBanner setImageBannerPageControl];
    return imageBanner;
}


- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        _scrollView = [[UIScrollView alloc] initWithFrame:frame];
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator   = NO;
        _scrollView.delegate = self;
        _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);
        _scrollView.contentSize = CGSizeMake(IMAGEBANNER_WIDTH*3, IMAGEBANNER_HEIGHT);
        _scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
        _scrollView.pagingEnabled = YES;
        _scrollView.bounces = NO;
        [self addSubview:_scrollView];

        _imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH*0, 0, IMAGEBANNER_WIDTH, IMAGEBANNER_HEIGHT)];
        _imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH*1, 0, IMAGEBANNER_WIDTH, IMAGEBANNER_HEIGHT)];
        _imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH*2, 0, IMAGEBANNER_WIDTH, IMAGEBANNER_HEIGHT)];

        [_scrollView addSubview:_imageView1];
        [_scrollView addSubview:_imageView2];
        [_scrollView addSubview:_imageView3];

        [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:@selector(setImageBannerAutoMove) userInfo:nil repeats:YES];
    }
    return self;
}

//设置图片轮播的图片
- (void)setImageBannerImages:(NSArray *)images
{
    index1 = images.count-1; //指向最后一张图片
    index2 = 0;              //指向最第一张图片
    index3 = 1;              //指向最第二张图片

    //如果只有一张图片,则禁止滑动
    if (images.count == 1) {
        _scrollView.scrollEnabled = NO;
        index3 = 0;
    }

    imageUrls = [[NSMutableArray alloc] init];

    for (NSString *string in images) {
        [imageUrls addObject:[NSURL URLWithString:string]];
    }

    [_imageView1 sd_setImageWithURL:imageUrls[index1] placeholderImage:_placeHolderImage];
    [_imageView2 sd_setImageWithURL:imageUrls[index2] placeholderImage:_placeHolderImage];
    [_imageView3 sd_setImageWithURL:imageUrls[index3] placeholderImage:_placeHolderImage];
}

//设置图片轮播自动滚动
- (void)setImageBannerAutoMove
{
    index1 = index1+1;
    index2 = index2+1;
    index3 = index3+1;

    if (index1 == imageUrls.count) {
        index1 = 0;
    }
    if (index2 == imageUrls.count) {
        index2 = 0;
    }
    if (index3 == imageUrls.count) {
        index3 = 0;
    }

    [_imageView1 sd_setImageWithURL:imageUrls[index1] placeholderImage:_placeHolderImage];
    [_imageView2 sd_setImageWithURL:imageUrls[index2] placeholderImage:_placeHolderImage];
    [_imageView3 sd_setImageWithURL:imageUrls[index3] placeholderImage:_placeHolderImage];

    _pageControl.currentPage = index2;
    _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);
}

//设置图片轮播的PageControl
- (void)setImageBannerPageControl
{
    _pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(IMAGEBANNER_WIDTH-imageUrls.count*10,  CGRectGetMaxY(_scrollView.frame)-20, 0, 20)];
    _pageControl.numberOfPages = imageUrls.count;
    _pageControl.backgroundColor = [UIColor redColor];
    _pageControl.currentPage = 0;
    [self addSubview:_pageControl];
}

#pragma mark - UIScrollView Delegate

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if (_scrollView.contentOffset.x == 0) { //左滑

        index1 = index1-1;
        index2 = index2-1;
        index3 = index3-1;

        if (index1 == -1) {
            index1 = imageUrls.count-1;
        }
        if (index2 == -1) {
            index2 = imageUrls.count-1;
        }
        if (index3 == -1) {
            index3 = imageUrls.count-1;
        }

    } else if (_scrollView.contentOffset.x == IMAGEBANNER_WIDTH*2) { //右滑

        index1 = index1+1;
        index2 = index2+1;
        index3 = index3+1;

        if (index1 == imageUrls.count) {
            index1 = 0;
        }
        if (index2 == imageUrls.count) {
            index2 = 0;
        }
        if (index3 == imageUrls.count) {
            index3 = 0;
        }
    } else return;

    [_imageView1 sd_setImageWithURL:imageUrls[index1] placeholderImage:_placeHolderImage];
    [_imageView2 sd_setImageWithURL:imageUrls[index2] placeholderImage:_placeHolderImage];
    [_imageView3 sd_setImageWithURL:imageUrls[index3] placeholderImage:_placeHolderImage];

    _pageControl.currentPage = index2;
    _scrollView.contentOffset = CGPointMake(IMAGEBANNER_WIDTH, 0);

}


@end

iOS 图片轮播_第3张图片

DEMO下载

你可能感兴趣的:(iOS开发)