一步一步拆解一个简单的iOS轮播图(三图)
导言(可以不看):
不吹不黑,也许是东半球最简单的iOS轮播图拆分注释(讲解不敢当)了(tree new bee)。(一句话包含两个人,你能猜到有谁吗?提示:一个在卖手机,一个最近在卖书)哈哈。。。
我第一次项目中需要使用轮播图的时候我是用的别人写好的一个轮子,那个轮播封装很多东西,包括比如可以设置pageControl的位置,可以传图片url或本地图片,缓存网络图片等等。但是我觉得没必要搞那么复杂,我喜欢简单并足够做事的东西。现在有时间便想自己把它拆解一下。看了一些简书上一些作者写的关于轮播图的讲解,我发现好多人写的其实是有问题的,虽然不易发现,但是你仔细测一下他的demo,很多都有问题。
我的这个轮播控件基本能满足现有市场上的所有app的轮播,反正我没见过把轮播搞得更花哨的,没太大意义。我自己把它的实现分为三块:1、添加基本控件,控制滚动(也就是控制scrollView,实现代理方法);2、自动滚动,timer;3、处理点击事件(代理)。代码注释很详细了,还看不懂的可以给我留言。
自定义一个View继承自UIView,这个类就是封装的轮播图类
先看看我们需要在初始化用语句中给这个自定义View添加哪些控件:scrollView、pageControl、三个按钮图(有self的应该知道是定义在在类拓展中的属性吧)
- (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中添加三个图片按钮,因为后面需要响应点击事件,所以我直接用按钮不用imageView了,感觉更方便一些
for (int i = ;i < imageBtnCount; i++) {
UIButton *imageBtn = [[UIButton alloc] init];
[scrollView addSubview:imageBtn];
}
//添加pageControl
UIPageControl *pageControl = [[UIPageControl alloc] init];
[self addSubview:pageControl];
self.pageControl = pageControl;
}
return self;
}
类拓展中的属性:(timer后面会需要,定时自动轮播)
@property (nonatomic, weak) UIScrollView*scrollView;
@property (nonatomic, weak) UIPageControl *pageControl;
@property (nonatomic, weak) NSTimer *timer;
接下来布局子控件:
布局子控件之前要先说一个东西:
static const int imageBtnCount = ;
这个count我们很多地方都会用到,因为这个轮播图的原理就是用三张图来实现无限循环轮播的假象。(#define能少用就少用吧啊)
//布局子控件
- (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是不一样的
if (self.isScrollDorectionPortrait) { //竖向
//contentSize要放三张图片
self.scrollView.contentSize = CGSizeMake(width, height * imageBtnCount);
} else { //横向
self.scrollView.contentSize = CGSizeMake(width * imageBtnCount, height);
}
//设置三张图片的位置,并为三个按钮添加点击事件
for (int i = ; i < imageBtnCount; i++) {
UIButton *imageBtn = self.scrollView.subviews[i];
[imageBtn addTarget:self action:@selector(imageBtnClick:) forControlEvents:UIControlEventTouchUpInside];
if (self.isScrollDorectionPortrait) { //竖向
imageBtn.frame = CGRectMake(, i * height, width, height);
} else { //横向
imageBtn.frame = CGRectMake(i * width, , width, height);
}
}
//设置contentOffset,显示最中间的图片
if (self.isScrollDorectionPortrait) { //竖向
self.scrollView.contentOffset = CGPointMake(, height);
} else { //横向
self.scrollView.contentOffset = CGPointMake(width, );
} //设置pageControl的位置
CGFloat pageW = ;
CGFloat pageH = ;
CGFloat pageX = width - pageW;
CGFloat pageY = height - pageH;
self.pageControl.frame = CGRectMake(pageX, pageY, pageW, pageH); }
接下来看一下对外接口:(代理暂时不用看,那是后面处理点击的事了)
#import <UIKit/UIKit.h> @class ATCarouselView;
@protocol ATCarouselViewDelegate <NSObject>
@optional
/**
* 点击图片的回调事件
*/
- (void)carouselView:(ATCarouselView *)carouselView indexOfClickedImageBtn:(NSUInteger)index;
@end @interface ATCarouselView : UIView
//传入图片数组
@property (nonatomic, copy) NSArray *images;
//pageControl颜色设置
@property (nonatomic, strong) UIColor *currentPageColor;
@property (nonatomic, strong) UIColor *pageColor;
//是否竖向滚动
@property (nonatomic, assign, getter=isScrollDorectionPortrait) BOOL scrollDorectionPortrait; @property (weak, nonatomic) id<ATCarouselViewDelegate> delegate;
@end
使用者需要设置的东西都在这里了:接下来看set方法:(pageControl的太简单就不占篇幅了)
//根据传入的图片数组设置图片
- (void)setImages:(NSArray *)images {
_images = images;
//pageControl的页数就是图片的个数
self.pageControl.numberOfPages = images.count;
//默认一开始显示的是第0页
self.pageControl.currentPage = ;
//设置图片显示内容
[self setContent];
//开启定时器
[self startTimer]; }
下面看setContent方法,设置显示内容,定时器在后面说:
//设置显示内容
- (void)setContent {
//设置三个imageBtn的显示图片
for (int i = ; i < self.scrollView.subviews.count; i++) {
//取出三个imageBtn
UIButton *imageBtn = self.scrollView.subviews[i];
//这个是为了给图片做索引用的
NSInteger index = self.pageControl.currentPage; if (i == ) { //第一个imageBtn,隐藏在当前显示的imageBtn的左侧
index--; //当前页索引减1就是第一个imageBtn的图片索引
} else if (i == ) { //第三个imageBtn,隐藏在当前显示的imageBtn的右侧
index++; //当前页索引加1就是第三个imageBtn的图片索引
}
//无限循环效果的处理就在这里
if (index < ) { //当上面index为0的时候,再向右拖动,左侧图片显示,这时候我们让他显示最后一张图片
index = self.pageControl.numberOfPages - ;
} else if (index == self.pageControl.numberOfPages) { //当上面的index超过最大page索引的时候,也就是滑到最右再继续滑的时候,让他显示第一张图片
index = ;
}
imageBtn.tag = index;
//用上面处理好的索引给imageBtn设置图片
[imageBtn setBackgroundImage:self.images[index] forState:UIControlStateNormal];
[imageBtn setBackgroundImage:self.images[index] forState:UIControlStateHighlighted]; }
}
先把原理图粘在这吧,对照着代码看可能更容易一点:

最后这个是最核心的步骤:

好了,接着看updateContent:
//状态改变之后更新显示内容
- (void)updateContent {
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
[self setContent];
//唯一跟设置显示内容不同的就是重新设置偏移量,让它永远用中间的按钮显示图片,滑动之后就偷偷的把偏移位置设置回去,这样就实现了永远用中间的按钮显示图片
//设置偏移量在中间
if (self.isScrollDorectionPortrait) {
self.scrollView.contentOffset = CGPointMake(, height);
} else {
self.scrollView.contentOffset = CGPointMake(width, );
}
}
后面就简单了,滚动的时候的一些操作:
//拖拽的时候执行哪些操作
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
//拖动的时候,哪张图片最靠中间,也就是偏移量最小,就滑到哪页
//用来设置当前页
NSInteger page = ;
//用来拿最小偏移量
CGFloat minDistance = MAXFLOAT;
//遍历三个imageView,看那个图片偏移最小,也就是最靠中间
for (int i = ; i < self.scrollView.subviews.count; i++) {
UIButton *imageBtn = self.scrollView.subviews[i];
CGFloat distance = ;
if (self.isScrollDorectionPortrait) {
distance = ABS(imageBtn.frame.origin.y - scrollView.contentOffset.y);
} else {
distance = ABS(imageBtn.frame.origin.x - scrollView.contentOffset.x);
}
if (distance < minDistance) {
minDistance = distance;
page = imageBtn.tag;
}
}
self.pageControl.currentPage = page;
} //结束拖拽的时候更新image内容
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self updateContent];
}
接下来就是定时器和代理设置点击事件了,这种比较简单的我就不多说了,我理解最难的地方都在上面的图里说明白了:
先说最简单的点击事件:.h文件
@class ATCarouselView;
@protocol ATCarouselViewDelegate <NSObject>
@optional
/**
* 点击图片的回调事件
*/
- (void)carouselView:(ATCarouselView *)carouselView indexOfClickedImageBtn:(NSUInteger)index;
@end
.m文件
- (void)imageBtnClick:(UIButton *)btn {
// NSLog(@"%ld",btn.tag);
if ([self.delegate respondsToSelector:@selector(carouselView:indexOfClickedImageBtn:)])
{
[self.delegate carouselView:self indexOfClickedImageBtn:btn.tag];
}
}
最后是定时器自动轮播的处理:
//开始计时器
- (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 height = self.bounds.size.height;
CGFloat width = self.bounds.size.width;
if (self.isScrollDorectionPortrait) {
[self.scrollView setContentOffset:CGPointMake(, * height) animated:YES];
} else {
[self.scrollView setContentOffset:CGPointMake( * width, ) animated:YES];
}
}
最后是使用这个轮播图:
- (void)viewDidLoad {
[super viewDidLoad];
ATCarouselView *carousel = [[ATCarouselView alloc] initWithFrame:CGRectMake(, , [UIScreen mainScreen].bounds.size.width, )];
carousel.delegate = self;
// carousel.scrollDorectionPortrait = YES;
carousel.images = @[
[UIImage imageNamed:@""],
[UIImage imageNamed:@""],
[UIImage imageNamed:@""],
[UIImage imageNamed:@""],
[UIImage imageNamed:@""]
];
carousel.currentPageColor = [UIColor orangeColor];
carousel.pageColor = [UIColor grayColor];
[self.view addSubview:carousel];
}
- (void)carouselView:(ATCarouselView *)carouselView indexOfClickedImageBtn:(NSUInteger )index {
NSLog(@"点击了第%ld张图片",index);
}
博客里把所有代码都贴上就太浪费空间了,基本上所有比较重要的都在上面了,如果还有不懂的可以看一下demo跑一下,有问题欢迎留言: my github:https://github.com/alan12138/carousel
一步一步拆解一个简单的iOS轮播图(三图)的更多相关文章
- 用Vue实现一个简单的图片轮播
本文已收录至https://github.com/likekk/studyBlog欢迎大家star,共同学习,共同进步.如果文章有错误的地方,欢迎大家指出.后期将在将GitHub上规划前端学习的路线和 ...
- viewPager+Handler+Timer简单实现广告轮播效果
基本思想是在Avtivity中放一个ViewPager,然后通过监听去实现联动效果,代码理由详细的解释,我就不说了. MainActivity.java package com.example.adm ...
- 前端(十七)—— jQuery基础:jQuery的基本使用、JQ功能概括、JS对象与JQ对象转换、Ajax简单应用、轮播图
jQuery的基本使用.JQ功能概括.JS对象与JQ对象转换.Ajax简单应用.轮播图 一.认识jQuery 1.什么是jQuery jQuery是对原生JavaScript二次封装的工具函数集合 j ...
- 【如何快速的开发一个简单的iOS直播app】(代码篇)
开篇([如何快速的开发一个完整的iOS直播app](原理篇)) 好久没写简书,因为好奇的我跑去学习直播了,今天就分享一下我的感慨. 目前为止直播还是比较热点的技术的,简书,git上有几篇阅读量和含金量 ...
- 用原生的javascript 实现一个无限滚动的轮播图
说一下思路:和我上一篇博客中用JQ去写的轮播图有相同点和不同点 相同点: 首先页面布局是一样的 同样是改变.inner盒子的位置去显示不同的图片 不同点: 为了实现无限滚动需要多添加两张重复的图片 左 ...
- 最简单的html轮播图制作适合新手
html代码 --------------------------------------------------------------------------------------------- ...
- 关于最近在做的一个js全屏轮播插件
最近去面试了,对方要求我在一个星期内用原生的js代码写一个全屏轮播的插件,第一想法就是跟照片轮播很相似,只是照片轮播是有定义一个宽高度大小已经确定了的容器用来存储所有照片,然后将照片全部左浮动,利用m ...
- 使用Handler和Timer+Timertask实现简单的图片轮播
布局文件就只放了一个简单的ImageView,就不展示了. 下面是Activity package com.example.administrator.handlerthreadmessagedemo ...
- 纯JS写最简单的图片轮播
非常简单的一个大图轮播,通过将控制显示位置来进行轮播效果,写来给正在学习的新手朋友们参考交流. 先看效果:(实际效果没有这么快) 先看布局: <div id="display" ...
随机推荐
- maven web项目中web.xml
web.xml 不是web工程必须的. web.xml文件用来配置那些东西:欢迎页,servlet,filter等. web.xml文件中定义了多少种标签元素,web.xml 中就可以出现它的模式文件 ...
- Powershell 字符串处理案例
有一张Excel表格收集了计算机名和IP地址,另外一张表有计算机名,需要找出这张表中计算机名对应的IP地址. #定义函数Get-LikeContentInfo function Get-LikeCon ...
- ExtJS扩展:扩展grid之toolbar button禁用表达式
在前一篇文章我们扩展了grid通过选中记录数来禁用toolbar上的按钮,有时候我们需要通过记录中的数据来决定是否禁用按钮,今天我们就来扩展它. 照例,最新的代码和例子都在gi ...
- 学习笔记: Delphi之线程类TThread
新的公司接手的第一份工作就是一个多线程计算的小系统.也幸亏最近对线程有了一些学习,这次一接手就起到了作用.但是在实际的开发过程中还是发现了许多的问题,比如挂起与终止的概念都没有弄明白,导致浪费许多的时 ...
- ASP.NET SignalR 高可用设计
在 One ASP.NET 的架构图中,微软将 WebAPI 和 SignalR 归类到 Services 类型与 MVC.Web Forms 同列为一等公民,未来的 ASP.NET 5 尽管还在be ...
- 让你的站点也支持MarkDown
Markdown是一种可以使用普通文本编辑器编写的标记语言,通过类似HTML的标记语法,它可以使普通文本内容具有一定的格式.Markdown的语法简洁明了.学习容易,而且功能比纯文本更强,因此有很多人 ...
- 在职场中混,"讲演稿"的重要性
背景: 在职场上工作的人,思维都像流水行云一样无拘无束.某时某刻说不定就会产生点“头脑风暴”. 但是在多数情总下,只是自己想想,跟朋友吹瞌子的时候随便说说,很少有用文字,图形把自己的思维给表现出来. ...
- padding标准盒模型和怪异盒子模型
我们都知道padding是为块级元素设置内边距 但是在使用过程中,我们却会遇到一些问题.padding的标准盒模型和怪异盒模型 padding盒子模型 我们通过demo来讲这个问题,用文字干讲第一没意 ...
- salesforce 零基础学习(四十七) 数据加密简单介绍
对于一个项目来说,除了稳定性以及健壮性以外,还需要有较好的安全性,此篇博客简单描述salesforce中关于安全性的一点小知识,特别感谢公司中的nate大神和鹏哥让我学到了新得知识. 项目简单背景: ...
- SQL Server 2014新特性探秘(2)-SSD Buffer Pool Extension
简介 SQL Server 2014中另一个非常好的功能是,可以将SSD虚拟成内存的一部分,来供SQL Server数据页缓冲区使用.通过使用SSD来扩展Buffer-Pool,可以使得大量随 ...