标签:
先看看我们需要在初始化用语句中给这个自定义View添加哪些控件:scrollView、pageControl、三个按钮图(有self的应该知道是定义在在类拓展中的属性吧)
1 - (instancetype)initWithFrame:(CGRect)frame { 2 if (self = [super initWithFrame:frame]) { 3 //定义一个scrollView,最主要的轮播控件 4 UIScrollView *scrollView = [[UIScrollView alloc] init]; 5 scrollView.delegate = self; 6 //横竖两种滚轮都不显示 7 scrollView.showsVerticalScrollIndicator = NO; 8 scrollView.showsHorizontalScrollIndicator = NO; 9 //需要分页 10 scrollView.pagingEnabled = YES; 11 //不需要回弹(试了一下加不加应该都没什么影响) 12 scrollView.bounces = NO; 13 [self addSubview:scrollView]; 14 self.scrollView = scrollView; 15 16 //在scrollView中添加三个图片按钮,因为后面需要响应点击事件,所以我直接用按钮不用imageView了,感觉更方便一些 17 for (int i = 0;i < imageBtnCount; i++) { 18 UIButton *imageBtn = [[UIButton alloc] init]; 19 [scrollView addSubview:imageBtn]; 20 } 21 //添加pageControl 22 UIPageControl *pageControl = [[UIPageControl alloc] init]; 23 [self addSubview:pageControl]; 24 self.pageControl = pageControl; 25 } 26 return self; 27 }
类拓展中的属性:(timer后面会需要,定时自动轮播)
1 @property (nonatomic, weak) UIScrollView*scrollView; 2 @property (nonatomic, weak) UIPageControl *pageControl; 3 @property (nonatomic, weak) NSTimer *timer;
接下来布局子控件:
布局子控件之前要先说一个东西:
1 static const int imageBtnCount = 3;
这个count我们很多地方都会用到,因为这个轮播图的原理就是用三张图来实现无限循环轮播的假象。(#define能少用就少用吧啊)
1 //布局子控件 2 - (void)layoutSubviews { 3 [super layoutSubviews]; 4 //设置scrollView的frame 5 self.scrollView.frame = self.bounds; 6 7 CGFloat width = self.bounds.size.width; 8 CGFloat height = self.bounds.size.height; 9 //设置contentSize,不同轮播方向的时候contentSize是不一样的 10 if (self.isScrollDorectionPortrait) { //竖向 11 //contentSize要放三张图片 12 self.scrollView.contentSize = CGSizeMake(width, height * imageBtnCount); 13 } else { //横向 14 self.scrollView.contentSize = CGSizeMake(width * imageBtnCount, height); 15 } 16 //设置三张图片的位置,并为三个按钮添加点击事件 17 for (int i = 0; i < imageBtnCount; i++) { 18 UIButton *imageBtn = self.scrollView.subviews[i]; 19 [imageBtn addTarget:self action:@selector(imageBtnClick:) forControlEvents:UIControlEventTouchUpInside]; 20 if (self.isScrollDorectionPortrait) { //竖向 21 imageBtn.frame = CGRectMake(0, i * height, width, height); 22 } else { //横向 23 imageBtn.frame = CGRectMake(i * width, 0, width, height); 24 } 25 } 26 //设置contentOffset,显示最中间的图片 27 if (self.isScrollDorectionPortrait) { //竖向 28 self.scrollView.contentOffset = CGPointMake(0, height); 29 } else { //横向 30 self.scrollView.contentOffset = CGPointMake(width, 0); 31 } 32 33 //设置pageControl的位置 34 CGFloat pageW = 100; 35 CGFloat pageH = 20; 36 CGFloat pageX = width - pageW; 37 CGFloat pageY = height - pageH; 38 self.pageControl.frame = CGRectMake(pageX, pageY, pageW, pageH); 39 40 }
接下来看一下对外接口:(代理暂时不用看,那是后面处理点击的事了)
1 #import <UIKit/UIKit.h> 2 3 @class ATCarouselView; 4 @protocol ATCarouselViewDelegate <NSObject> 5 @optional 6 /** 7 * 点击图片的回调事件 8 */ 9 - (void)carouselView:(ATCarouselView *)carouselView indexOfClickedImageBtn:(NSUInteger)index; 10 @end 11 12 @interface ATCarouselView : UIView 13 //传入图片数组 14 @property (nonatomic, copy) NSArray *images; 15 //pageControl颜色设置 16 @property (nonatomic, strong) UIColor *currentPageColor; 17 @property (nonatomic, strong) UIColor *pageColor; 18 //是否竖向滚动 19 @property (nonatomic, assign, getter=isScrollDorectionPortrait) BOOL scrollDorectionPortrait; 20 21 @property (weak, nonatomic) id<ATCarouselViewDelegate> delegate; 22 @end
使用者需要设置的东西都在这里了:接下来看set方法:(pageControl的太简单就不占篇幅了)
1 //根据传入的图片数组设置图片 2 - (void)setImages:(NSArray *)images { 3 _images = images; 4 //pageControl的页数就是图片的个数 5 self.pageControl.numberOfPages = images.count; 6 //默认一开始显示的是第0页 7 self.pageControl.currentPage = 0; 8 //设置图片显示内容 9 [self setContent]; 10 //开启定时器 11 [self startTimer]; 12 13 }
下面看setContent方法,设置显示内容,定时器在后面说:
1 //设置显示内容 2 - (void)setContent { 3 //设置三个imageBtn的显示图片 4 for (int i = 0; i < self.scrollView.subviews.count; i++) { 5 //取出三个imageBtn 6 UIButton *imageBtn = self.scrollView.subviews[i]; 7 //这个是为了给图片做索引用的 8 NSInteger index = self.pageControl.currentPage; 9 10 if (i == 0) { //第一个imageBtn,隐藏在当前显示的imageBtn的左侧 11 index--; //当前页索引减1就是第一个imageBtn的图片索引 12 } else if (i == 2) { //第三个imageBtn,隐藏在当前显示的imageBtn的右侧 13 index++; //当前页索引加1就是第三个imageBtn的图片索引 14 } 15 //无限循环效果的处理就在这里 16 if (index < 0) { //当上面index为0的时候,再向右拖动,左侧图片显示,这时候我们让他显示最后一张图片 17 index = self.pageControl.numberOfPages - 1; 18 } else if (index == self.pageControl.numberOfPages) { //当上面的index超过最大page索引的时候,也就是滑到最右再继续滑的时候,让他显示第一张图片 19 index = 0; 20 } 21 imageBtn.tag = index; 22 //用上面处理好的索引给imageBtn设置图片 23 [imageBtn setBackgroundImage:self.images[index] forState:UIControlStateNormal]; 24 [imageBtn setBackgroundImage:self.images[index] forState:UIControlStateHighlighted]; 25 26 } 27 }
先把原理图粘在这吧,对照着代码看可能更容易一点:
最后这个是最核心的步骤:
好了,接着看updateContent:
1 //状态改变之后更新显示内容 2 - (void)updateContent { 3 CGFloat width = self.bounds.size.width; 4 CGFloat height = self.bounds.size.height; 5 [self setContent]; 6 //唯一跟设置显示内容不同的就是重新设置偏移量,让它永远用中间的按钮显示图片,滑动之后就偷偷的把偏移位置设置回去,这样就实现了永远用中间的按钮显示图片 7 //设置偏移量在中间 8 if (self.isScrollDorectionPortrait) { 9 self.scrollView.contentOffset = CGPointMake(0, height); 10 } else { 11 self.scrollView.contentOffset = CGPointMake(width, 0); 12 } 13 }
后面就简单了,滚动的时候的一些操作:
1 //拖拽的时候执行哪些操作 2 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 3 //拖动的时候,哪张图片最靠中间,也就是偏移量最小,就滑到哪页 4 //用来设置当前页 5 NSInteger page = 0; 6 //用来拿最小偏移量 7 CGFloat minDistance = MAXFLOAT; 8 //遍历三个imageView,看那个图片偏移最小,也就是最靠中间 9 for (int i = 0; i < self.scrollView.subviews.count; i++) { 10 UIButton *imageBtn = self.scrollView.subviews[i]; 11 CGFloat distance = 0; 12 if (self.isScrollDorectionPortrait) { 13 distance = ABS(imageBtn.frame.origin.y - scrollView.contentOffset.y); 14 } else { 15 distance = ABS(imageBtn.frame.origin.x - scrollView.contentOffset.x); 16 } 17 if (distance < minDistance) { 18 minDistance = distance; 19 page = imageBtn.tag; 20 } 21 } 22 self.pageControl.currentPage = page; 23 } 24 25 //结束拖拽的时候更新image内容 26 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 27 { 28 [self updateContent]; 29 }
先说最简单的点击事件:.h文件
1 @class ATCarouselView; 2 @protocol ATCarouselViewDelegate <NSObject> 3 @optional 4 /** 5 * 点击图片的回调事件 6 */ 7 - (void)carouselView:(ATCarouselView *)carouselView indexOfClickedImageBtn:(NSUInteger)index; 8 @end
.m文件
1 - (void)imageBtnClick:(UIButton *)btn { 2 // NSLog(@"%ld",btn.tag); 3 if ([self.delegate respondsToSelector:@selector(carouselView:indexOfClickedImageBtn:)]) 4 { 5 [self.delegate carouselView:self indexOfClickedImageBtn:btn.tag]; 6 } 7 8 }
最后是定时器自动轮播的处理:
1 //开始计时器 2 - (void)startTimer { 3 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextImage) userInfo:nil repeats:YES]; 4 [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 5 self.timer = timer; 6 } 7 //停止计时器 8 - (void)stopTimer { 9 //结束计时 10 [self.timer invalidate]; 11 //计时器被系统强引用,必须手动释放 12 self.timer = nil; 13 } 14 //通过改变contentOffset * 2换到下一张图片 15 - (void)nextImage { 16 CGFloat height = self.bounds.size.height; 17 CGFloat width = self.bounds.size.width; 18 if (self.isScrollDorectionPortrait) { 19 [self.scrollView setContentOffset:CGPointMake(0, 2 * height) animated:YES]; 20 } else { 21 [self.scrollView setContentOffset:CGPointMake(2 * width, 0) animated:YES]; 22 } 23 }
最后是使用这个轮播图:
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 ATCarouselView *carousel = [[ATCarouselView alloc] initWithFrame:CGRectMake(0, 20, [UIScreen mainScreen].bounds.size.width, 300)]; 4 carousel.delegate = self; 5 // carousel.scrollDorectionPortrait = YES; 6 carousel.images = @[ 7 [UIImage imageNamed:@"0"], 8 [UIImage imageNamed:@"1"], 9 [UIImage imageNamed:@"2"], 10 [UIImage imageNamed:@"3"], 11 [UIImage imageNamed:@"4"] 12 ]; 13 carousel.currentPageColor = [UIColor orangeColor]; 14 carousel.pageColor = [UIColor grayColor]; 15 [self.view addSubview:carousel]; 16 17 } 18 - (void)carouselView:(ATCarouselView *)carouselView indexOfClickedImageBtn:(NSUInteger )index { 19 NSLog(@"点击了第%ld张图片",index); 20 }
博客里把所有代码都贴上就太浪费空间了,基本上所有比较重要的都在上面了,如果还有不懂的可以看一下demo跑一下,有问题欢迎留言: my github:https://github.com/alan12138/carousel
标签:
原文地址:http://www.cnblogs.com/alan12138/p/5641186.html