一步一步拆解一个简单的iOS轮播图(三图)

导言(可以不看):

不吹不黑,也许是东半球最简单的iOS轮播图拆分注释(讲解不敢当)了(tree new bee)。(一句话包含两个人,你能猜到有谁吗?提示:一个在卖手机,一个最近在卖书)哈哈。。。

我第一次项目中需要使用轮播图的时候我是用的别人写好的一个轮子,那个轮播封装很多东西,包括比如可以设置pageControl的位置,可以传图片url或本地图片,缓存网络图片等等。但是我觉得没必要搞那么复杂,我喜欢简单并足够做事的东西。现在有时间便想自己把它拆解一下。看了一些简书上一些作者写的关于轮播图的讲解,我发现好多人写的其实是有问题的,虽然不易发现,但是你仔细测一下他的demo,很多都有问题。

我的这个轮播控件基本能满足现有市场上的所有app的轮播,反正我没见过把轮播搞得更花哨的,没太大意义。我自己把它的实现分为三块:1、添加基本控件,控制滚动(也就是控制scrollView,实现代理方法);2、自动滚动,timer;3、处理点击事件(代理)。代码注释很详细了,还看不懂的可以给我留言。

自定义一个View继承自UIView,这个类就是封装的轮播图类

先看看我们需要在初始化用语句中给这个自定义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

时间: 2024-10-13 09:48:04

一步一步拆解一个简单的iOS轮播图(三图)的相关文章

一个简单的图片轮播效果,

<!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> </head> <style type="text/css"> .asd{ background-color:#1ADC9D; border:2px solid:#F30A0E; width:25px; height:25px

一个简单的图片轮播功能(图片自动播放,点击控件可直接跳转对对应图片)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-

简单的图片轮播效果

用js代码来实现一个简单的图片轮播效果 鼠标移入图片后显示左右箭头按钮,点击就可以实现图片的切换. 以下分别是html代码和js代码,欢迎批评和讨论! <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>图片轮播</title> <style> *{padding:0px; margin:0px;

viewPager+Handler+Timer简单实现广告轮播效果

基本思想是在Avtivity中放一个ViewPager,然后通过监听去实现联动效果,代码理由详细的解释,我就不说了. MainActivity.java 1 package com.example.administrator.imageviewlunbodemo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.os.Message

js最简单焦点图片轮播代码

将下面代码保存为banner.js,在需要显示焦点图的地方调用该js即可. <script type="text/javascript" src="banner.js"></script> 注意:代码中图片路径修改为你自己的图片地址 var widths=725; //焦点图片宽 var w=0; var widthss=widths+w; var heights=295; //焦点图片高 var heightss=heightss+w; v

记一个好用的轮播图的FlexSlider

之前给自己公司的主页套用过一个js动态生成的轮播图,但是从载入的时机和载入后的效果都不太理想,又懒得去优化了,这次偶然遇到一个比较不错的轮播图的js插件,记录之. 首先它是给予jquery的,引进jquery和jquery.flexslider-min.js,然后定义div范围,在div内定义好要轮播的ul和li标签,然后设置flex参数渲染即可.不多说,上代码 <html> <body> <div class="flexslider"> <u

用原生的javascript 实现一个无限滚动的轮播图

说一下思路:和我上一篇博客中用JQ去写的轮播图有相同点和不同点 相同点: 首先页面布局是一样的 同样是改变.inner盒子的位置去显示不同的图片 不同点: 为了实现无限滚动需要多添加两张重复的图片 左右切换和前面的方法有所不同,前面是获取当前的索引值乘以-600px当做位移距离,现在是需要获取当前.inner的位置来加上或者减去-600来实现 下面来一步步的去实现轮播图: 首先是html <!DOCTYPE html> <html lang="en"> <

分别用css3、JS实现图片简单的无缝轮播功效

本文主要介绍分别使用CSS3.JS实现图片简单无缝轮播功效: 一.使用CSS3实现:利用animation属性 (实现一张一张的轮播,肉眼只看见一张图片) HTML部分比较简单,两个div下包着几个img标签:为了实现无缝轮播,注意第一张图片要与最后一张图片相同: <div class="out"> <div class="imgs"> <img src="img/beatuy.jpg"/> <img s

最简单的html轮播图制作适合新手

html代码 ---------------------------------------------------------------------------------------------------------------------- <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Typ