上一篇已经介绍了UIScrollView的使用,并且简单的写了一个轮播图的例子,但是上一篇中的轮播图不能自动滚动,也不能循环轮播。今天有时间整理一下自动循环轮播的真正轮播图实例。
循环轮播图的原理
实现循环轮播的原理现在常用的有两种:
1、创建n+2个滚动子视图。在滚动到首尾时候进行转换调整。
2、创建3个滚动子视图,每次滚动结束进行图片位置调整。
本实例代码使用第二种方式,创建3个滚动子视图。
废话不多说,直接上代码,在代码中做了详细的注释。
自定义类CustomerView.h文件代码
#import <UIKit/UIKit.h>
//声明协议,给轮播图的点击添加一个代理
//这里使用@class是因为下面的代理方法需要使用到本类。
@class CustomerView;
@protocol CustomerViewDelegate<NSObject>
@optional
//代理方法 点击图片回调
- (void)customerView:(CustomerView *)view indexOfClickedImage:(NSInteger)index;
@end
@interface CustomerView : UIView
//传入用于轮播图的图片数组
@property (nonatomic, copy)NSArray *images;
//pageControl颜色设置
@property (nonatomic, strong) UIColor *currentPageColor;
@property (nonatomic, strong) UIColor *pageColor;
//代理
@property (nonatomic, weak) id<CustomerViewDelegate> delegate;
@end
自定义类CustomerView.m文件代码
#import "CustomerView.h"
//轮播图创建的图片数量
static const int imageCount = 3;
//准守UIScrollViewDelegate协议
@interface CustomerView ()<UIScrollViewDelegate>
//定义全局变量在后面的自动轮播方法中使用
@property (nonatomic, weak)UIScrollView *scrollView;
@property (nonatomic, weak)UIPageControl *pageControl;
//定时器,用于自动轮播
@property (nonatomic, weak)NSTimer *timer;
@end
@implementation CustomerView
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//定义一个scrollView,最主要的轮播控件
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.delegate = self;
//横竖两种滚轮都不显示
scrollView.showsVerticalScrollIndicator = NO;
scrollView.showsHorizontalScrollIndicator = NO;
//需要分页
scrollView.pagingEnabled = YES;
//不需要回弹
scrollView.bounces = NO;
[self addSubview:scrollView];
self.scrollView = scrollView;
//在scrollView中添加三个图片
for (int i = 0;i < imageCount; i++) {
UIImageView *imageView = [[UIImageView alloc] init];
[scrollView addSubview:imageView];
}
//添加pageControl
UIPageControl *pageControl = [[UIPageControl alloc] init];
[self addSubview:pageControl];
self.pageControl = pageControl;
}
return self;
}
//布局子控件
- (void)layoutSubviews {
[super layoutSubviews];
//设置scrollView的frame
self.scrollView.frame = self.bounds;
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
//设置contentSize,不同轮播方向的时候contentSize是不一样的
//contentSize要放三张图片
self.scrollView.contentSize = CGSizeMake(width * imageCount, height);
//设置三张图片的位置,并为三个图片添加点击事件
for (int i = 0; i < imageCount; i++) {
UIImageView *imageView = self.scrollView.subviews[i];
//给图片添加点击手势
imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewClick:)];
[imageView addGestureRecognizer:tap];
//设置图片大小,和View相同
imageView.frame = CGRectMake(i * width, 0, width, height);
}
//设置contentOffset,显示最中间的图片
self.scrollView.contentOffset = CGPointMake(width, 0);
//设置pageControl的位置
CGFloat pageW = 100;
CGFloat pageH = 20;
CGFloat pageX = width/2-50;
CGFloat pageY = height - pageH;
self.pageControl.frame = CGRectMake(pageX, pageY, pageW, pageH);
}
//设置pageControl的CurrentPageColor
- (void)setCurrentPageColor:(UIColor *)currentPageColor {
_currentPageColor = currentPageColor;
self.pageControl.currentPageIndicatorTintColor = currentPageColor;
}
//设置pageControl的pageColor
- (void)setPageColor:(UIColor *)pageColor {
_pageColor = pageColor;
self.pageControl.pageIndicatorTintColor = pageColor;
}
//根据传入的图片数组设置图片
- (void)setImages:(NSArray *)images {
_images = images;
//pageControl的页数就是图片的个数
self.pageControl.numberOfPages = images.count;
//默认一开始显示的是第0页
self.pageControl.currentPage = 0;
//设置图片显示内容
[self setContent];
//开启定时器
[self startTimer];
}
//设置显示内容
- (void)setContent {
//设置三个imageBtn的显示图片
for (int i = 0; i < self.scrollView.subviews.count; i++) {
//取出三个imageView
UIImageView *imageView = self.scrollView.subviews[i];
//这个是为了给图片做索引用的
NSInteger index = self.pageControl.currentPage;
if (i == 0) { //第一个imageView,隐藏在当前显示的imageView的左侧
index--; //当前页索引减1就是第一个imageView的图片索引
} else if (i == 2) { //第三个imageView,隐藏在当前显示的imageView的右侧
index++; //当前页索引加1就是第三个imageView的图片索引
}
//无限循环效果的处理就在这里
if (index < 0) { //当上面index为0的时候,再向右拖动,左侧图片显示,这时候我们让他显示最后一张图片
index = self.pageControl.numberOfPages - 1;
} else if (index == self.pageControl.numberOfPages) { //当上面的index超过最大page索引的时候,也就是滑到最右再继续滑的时候,让他显示第一张图片
index = 0;
}
imageView.tag = index;
//用上面处理好的索引给imageView设置图片
imageView.image = self.images[index];
}
}
//状态改变之后更新显示内容
- (void)updateContent {
CGFloat width = self.bounds.size.width;
[self setContent];
//唯一跟设置显示内容不同的就是重新设置偏移量,让它永远用中间的按钮显示图片,滑动之后就偷偷的把偏移位置设置回去,这样就实现了永远用中间的按钮显示图片
//设置偏移量在中间
self.scrollView.contentOffset = CGPointMake(width, 0);
}
#pragma mark - UIScrollViewDelegate
//拖拽的时候执行哪些操作
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
//拖动的时候,哪张图片最靠中间,也就是偏移量最小,就滑到哪页
//用来设置当前页
NSInteger page = 0;
//用来拿最小偏移量
CGFloat minDistance = MAXFLOAT;
//遍历三个imageView,看那个图片偏移最小,也就是最靠中间
for (int i = 0; i < self.scrollView.subviews.count; i++) {
UIImageView *imageView = self.scrollView.subviews[i];
CGFloat distance = 0;
distance = ABS(imageView.frame.origin.x - scrollView.contentOffset.x);
if (distance < minDistance) {
minDistance = distance;
page = imageView.tag;
}
}
self.pageControl.currentPage = page;
}
//开始拖拽的时候停止计时器
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self stopTimer];
}
//结束拖拽的时候开始定时器
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[self startTimer];
}
//结束拖拽的时候更新image内容
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self updateContent];
}
//scroll滚动动画结束的时候更新image内容
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[self updateContent];
}
#pragma mark - 定时器
//开始计时器
- (void)startTimer {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.timer = timer;
}
//停止计时器
- (void)stopTimer {
//结束计时
[self.timer invalidate];
//计时器被系统强引用,必须手动释放
self.timer = nil;
}
//通过改变contentOffset * 2换到下一张图片
- (void)nextImage {
CGFloat width = self.bounds.size.width;
[self.scrollView setContentOffset:CGPointMake(2 * width, 0) animated:YES];
}
- (void)imageViewClick:(UITapGestureRecognizer *)tap {
// NSLog(@"%ld",btn.tag);
UIView *imageView = tap.view;
if ([self.delegate respondsToSelector:@selector(customerView:indexOfClickedImage:)])
{
[self.delegate customerView:self indexOfClickedImage:imageView.tag];
}
}
@end
ViewController中调用代码
#import "ViewController.h"
#import "CustomerView.h"
//遵守协议
@interface ViewController ()<CustomerViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建视图
CustomerView *customerView = [[CustomerView alloc] initWithFrame:CGRectMake(0, 20, [UIScreen mainScreen].bounds.size.width, 300)];
//设置代理
customerView.delegate = self;
//图片数组赋值
customerView.images = @[
[UIImage imageNamed:@"image_0.jpg"],
[UIImage imageNamed:@"image_1.jpg"],
[UIImage imageNamed:@"image_2.jpg"],
[UIImage imageNamed:@"image_3.jpg"]
];
customerView.currentPageColor = [UIColor whiteColor];
customerView.pageColor = [UIColor blueColor];
[self.view addSubview:customerView];
}
#pragma mark--CustomerViewDelegate 实现代理方法
- (void)customerView:(CustomerView *)view indexOfClickedImage:(NSInteger)index{
NSLog(@"点击了第%ld张图片",index);
}
@end
全部代码都已经贴出来了,就不再上传demo了。