一步一步拆解一个简单的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" ...
随机推荐
- js面向对象基础总结
js中如何定义一个类? 定义的function就是一个构造方法也就是说是定义了一个类:用这个方法可以new新对象出来. function Person(name, age){ this.name = ...
- 科学计算软件——Octave安装
Octave是一个旨在提供与Matlab语法兼容的开放源代码科学计算及数值分析的工具,是Matlab商业软件的一个强有力的竞争产品. 参考:[ML:Octave Installation] Gener ...
- Tempdb initial size和dbcc shrinkfile
在使用sql server时您可能遇到过下面的情况,tempdb的数据文件初始大小为3mb, 随着对tempdb的使用,tempdb文件逐渐变得很大(例如30GB),导致了磁盘空间不足. 此时您需要立 ...
- Android安全开发之浅谈密钥硬编码
Android安全开发之浅谈密钥硬编码 作者:伊樵.呆狐@阿里聚安全 1 简介 在阿里聚安全的漏洞扫描器中和人工APP安全审计中,经常发现有开发者将密钥硬编码在Java代码.文件中,这样做会引起很大风 ...
- CI-持续集成(2)-软件工业“流水线”技术实现
1 概述 持续集成(Continuous Integration)是一种软件开发实践.在本系列文章的前一章节已经对其背景及理论体系进行了介绍.本小节则承接前面提出的理论构想进行具体的技术实现. & ...
- Nodejs之MEAN栈开发(二)----视图与模型
上一节做了对Express做了简单的介绍,提出了controller,介绍了路由.这一节将重点放到视图和模型上,完成几个静态页面并部署到heroku上. 导航 前端布局使用bootstrap,从官网下 ...
- 译文---C#堆VS栈(Part One)
前言 本文主要是讲解C#语言在内存中堆.栈的使用情况,使读者能更好的理解值类型.引用类型以及线程栈.托管堆. 首先感谢原文作者:Matthew Cochran 为我们带来了一篇非常好的文章,并配以大量 ...
- S1293和S2220KTV项目结束
1.界面原型(前台的界面搭建一下) 2.数据库 3.架构设计 4.约定的文件抽取 2015年7月20日下午 歌星点歌三界面的联动,数据动态加载 01.点击第一个LIstView,弹出第二个ListVi ...
- Mac安装Bower
1.安装bower,得首先安装node: brew install npm //npm是nodejs的程序包管理器,如果安装过nodejs,可忽略此步. 2.安装Git(因为需要从Git仓库获取一些代 ...
- xamarin UWP自定义圆角按钮
uwp自带的button本身不支持圆角属性,所以要通过自定义控件实现. 通过设置Button的Background=“{x:Null}”设置为Null使背景为空,再设置Button.Content中的 ...