导言(可以不看):

不吹不黑,也许是东半球最简单的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轮播图(三图)的更多相关文章

  1. 用Vue实现一个简单的图片轮播

    本文已收录至https://github.com/likekk/studyBlog欢迎大家star,共同学习,共同进步.如果文章有错误的地方,欢迎大家指出.后期将在将GitHub上规划前端学习的路线和 ...

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

    基本思想是在Avtivity中放一个ViewPager,然后通过监听去实现联动效果,代码理由详细的解释,我就不说了. MainActivity.java package com.example.adm ...

  3. 前端(十七)—— jQuery基础:jQuery的基本使用、JQ功能概括、JS对象与JQ对象转换、Ajax简单应用、轮播图

    jQuery的基本使用.JQ功能概括.JS对象与JQ对象转换.Ajax简单应用.轮播图 一.认识jQuery 1.什么是jQuery jQuery是对原生JavaScript二次封装的工具函数集合 j ...

  4. 【如何快速的开发一个简单的iOS直播app】(代码篇)

    开篇([如何快速的开发一个完整的iOS直播app](原理篇)) 好久没写简书,因为好奇的我跑去学习直播了,今天就分享一下我的感慨. 目前为止直播还是比较热点的技术的,简书,git上有几篇阅读量和含金量 ...

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

    说一下思路:和我上一篇博客中用JQ去写的轮播图有相同点和不同点 相同点: 首先页面布局是一样的 同样是改变.inner盒子的位置去显示不同的图片 不同点: 为了实现无限滚动需要多添加两张重复的图片 左 ...

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

    html代码 --------------------------------------------------------------------------------------------- ...

  7. 关于最近在做的一个js全屏轮播插件

    最近去面试了,对方要求我在一个星期内用原生的js代码写一个全屏轮播的插件,第一想法就是跟照片轮播很相似,只是照片轮播是有定义一个宽高度大小已经确定了的容器用来存储所有照片,然后将照片全部左浮动,利用m ...

  8. 使用Handler和Timer+Timertask实现简单的图片轮播

    布局文件就只放了一个简单的ImageView,就不展示了. 下面是Activity package com.example.administrator.handlerthreadmessagedemo ...

  9. 纯JS写最简单的图片轮播

    非常简单的一个大图轮播,通过将控制显示位置来进行轮播效果,写来给正在学习的新手朋友们参考交流. 先看效果:(实际效果没有这么快) 先看布局: <div id="display" ...

随机推荐

  1. 数组和链表--Java学习笔记(一)

    版权声明: 本文由Faye_Zuo发布于http://www.cnblogs.com/zuofeiyi/, 本文可以被全部的转载或者部分使用,但请注明出处. 我是一个全职妈妈,两年前在上海一家人力资源 ...

  2. 关于Java8函数式编程你需要了解的几点

    函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异. 函数作为一等公民 在理解函数作为一等公民这句话时,让我们先来看一下一种非常常 ...

  3. 一个基于Orchard的开源CRM --coevery简介

    Coevery是开源的.NET Web平台项目,力争打造一个开放而鲁棒的CRM系统,采用Orchard架构,并使用AngularJS改善页面体验.作为一个后发优势的CRM 产品,Coevery 具有一 ...

  4. Nova PhoneGap框架 第八章 滚动条

    你可能会疑惑为什么滚动条这么常见的功能会在这里单独列出,但如果你有过PhoneGap开发经验的话,你就会发现要在Android 2.3 里面实现滚动条那真不是一件容易的事. 8.1 概述 目前主流的P ...

  5. [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)

    [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...

  6. 小型文件数据库 (a file database for small apps) SharpFileDB

    小型文件数据库 (a file database for small apps) SharpFileDB For english version of this article, please cli ...

  7. 《3D Math Primer for Graphics and Game Development》读书笔记1

    <3D Math Primer for Graphics and Game Development>读书笔记1 本文是<3D Math Primer for Graphics and ...

  8. OAuth2 Backend Web Application 验证过程

    本文是从我的 github 博客转载的,原文请看. 一图胜千言.图片请自由转载,请保留图片的原始签名.

  9. 使用变量 数据类型转换 逻辑控制语句(begin ...end; case...end; if...else; while)

    一:变量 变量分为局部变量和全局变量  (全局变量是系统自定的,是不可手动给值的,若想自己定义全局变量可考虑创建全局临时表!) 局部变量的定义:  declare @变量名  数据类型 (局部变量只能 ...

  10. web项目ajax技术一些总结

    WEB项目中,最主要的就是前后端间的联络.有时需要不进行页面跳转的前提下请求后端方法(action),就需要用到ajax. 在这个博客中,我用到的都是原生的js的ajax,不是很喜欢用jquery的a ...