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