您的位置:首页 > 产品设计 > UI/UE

iOS开发--轮播图(无限循环)新玩法--视差轮播--无限循环的新思路(两个UIImageView足矣)

2016-03-08 15:34 585 查看
老赖本来说要更新iOS学习课程,可走神弄了一下午的轮播图,这里把整个过程记录下来吧,对有需要的好朋友,或许有点儿小小的帮助.

首先说,轮播图,这个东西,做开发的应该都会,不过实现的的逻辑思路有所不同.

方法一

比方说固定有5张图,最基本的实现方法就是,在scrollView上添加7张图片,排列方式是:

五一二三四五一

当图片滑到最左边,也就是”五”这个图片的时候,让scrollView调用setContentOffset 这个方法,直接跳转到倒数第二个张上面,也是”五”,所以肉眼是看不出scrollView移动的.

注意这里的跳,不能开启动画.同理,当scrollView滑到最后一个,让它瞬间跳到左边的”一”这张上去…这样就实现了无限循环滚动.

这个方法,非常巧妙,平面理解有点儿费劲,不过老赖理解之—>你拿张纸条,让它收尾相连,形成一个空间的圆圈闭环,这样,估计你瞬间里明白了这个瞬间跳转是怎么回事了!

先来看看这种方法的效果图:如下



看了上面的效果图后,问题也就出来了,这里有少量的图片,可以直接把图片的imageView加载到scrollView上.但是,如果图片多呢,50张?100张?创建100个imageView加到scrollView上,内存也吃不消吧.

对了,有好朋友,应该想到了,可以用–复用机制!

方法二:

复用机制的好处,在tableView用的淋漓尽致,做轮播图,咱们用它的亲戚,collectionView.因为系统空间本身就有复用机制,无论你加载多少照片,它只创建了两个cell,这样就大大的优化APP性能.

具体的详情,老赖这里不详细阐述了,照旧贴出相关链接:

http://www.cnblogs.com/wendingding/p/3890850.html

http://www.cnblogs.com/wendingding/p/3890953.html

这两篇是有关collectionView做轮播图的,当然,这是大神的系列,好朋友们,可以翻看前后篇,系统的学习一下.

方法三:
说了这么多,老赖的粉丝都等急了吧,哈哈,别说话,先那啥…..呃,上效果图呗!



看了效果图,有好朋友说,搞什么啊,跟上面不是一毛一样的么?还没有人家的360°旋转切换文字呢!!

咳咳,那不是重点好么,重点是切换下一张图片,是有视差效果!!!视差效果!!!视差效果!!

一张图片压着另一张图片上面,这个效果在iPhone里就有,好像是调用系统相册里面切换照片时候的效果.

另外,重点来了!!!无论你多少张照片,老赖我这里只有用了两个UIImageView!没错,就是两个,一样完成无限轮播!

这个*不能装过咯,老赖我还是低调的,其实思路也很清晰,接下来,就听有北京著名赖人—-老赖娓娓道来之:

首先咱们还是先声明一些属性

//滚动视图
@property (nonatomic, strong) UIscrollView   *scrollView;
//第一个图片
@property (nonatomic, strong) UIImageView    *firstImageView;
//第二个图片
@property (nonatomic, strong) UIImageView    *secondImageView;
//图片的数据源
@property (nonatomic, strong) NSMutableArray *imageDataSource;
//页面控制器
@property (nonatomic, strong) UIPageControl  *pageController;
//时间控制器
@property (nonatomic, strong) NSTimer        *timer;


有好朋友打眼一看,就知道老赖不专业了,整两张图片的ImageView,命名竟然用first和second这low的名字.好吧,老赖承认,时间紧,任务重,各种原因吧,总之就这样了,爱咋咋地,跟老赖讲道理,嗯哼?

接下来就是对对象的初始化,老赖给代码都注释了

//添加滚动视图
UIscrollView *scrollView = [[UIscrollView alloc] initWithFrame:self.bounds];
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.pagingEnabled = YES;
scrollView.contentOffset = CGPointMake(CGRectGetWidth(self.frame), 0);
scrollView.delegate = self;
scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.frame) * 3, CGRectGetHeight(self.frame));
//添加点击事件
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick:)];
[scrollView addGestureRecognizer:tap];
//添加到父视图上
[self addSubview:scrollView];
self.scrollView = scrollView;

//给滚动视图添加两个UIImageView
UIImageView *firstImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
self.firstImageView = firstImageView;
UIImageView *secondImageView = [[UIImageView alloc] initWithFrame:self.bounds];
self.secondImageView = secondImageView;

[self.scrollView addSubview:secondImageView];
[self.scrollView addSubview:firstImageView];

//先设置第一张图片的位置,在滚动视图的正中央
self.firstImageView.frame = CGRectMake(CGRectGetWidth(self.frame), 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));

//设置pageController(这里可以根据个人喜好自定义)
UIPageControl *pageController = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), 20)];

pageController.center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) - 20);

//设置pageController 的页数
pageController.numberOfPages = self.imageDataSource.count;

pageController.currentPage = 0;
[self addSubview:pageController];
self.pageController = pageController;


.

其实老赖不喜欢,贴这么多代码…好多好朋友都跟老赖一样,代码一多,看不下去.那老赖我就把重要部分的代码,简短的贴出来,并解释说明之,最后,会把源码地址贴出来.

这里要说明一下刚才那一长串代码,scrollView的contentSize是3个屏幕宽,并且初始让scrollView的可视部分在最中间, 正好可以用来左右滚动.

然后,第一张图片的放到最中间,并且,它的位置是固定的,也就是说,第一张图片跟随scrollView滚动.

那第二张图片,就扮演滑动图片后,将要出现的视图了.

首先假定我们手指,从左往右滑动,也就是看看左边的图片,就是上一张图片:

//添加将要出现的视图(其实就是第二张图片)的中心点X坐标
static float nextImageViewCenterX = 0.f;

//判断滚动方向
if (scrollView.contentOffset.x < CGRectGetWidth(self.frame)) {
//从左往右
nextImageViewCenterX = (CGRectGetWidth(self.frame)  + offsetX) / 2;
_nextImageIndex = _currentImageIndex - 1;
}


.
上面的代码是用来干嘛的呢?有好朋友一样就看出来了!对,就是让第二张图片跟随第一张图片也就是scrollView移动的:

GPoint center = CGPointMake(nextImageViewCenterX , CGRectGetHeight(self.frame)/2);
//实时更新第二张图片的位置
self.secondImageView.center = center ;


.
那有好朋友要问了,为什么第二张图片的中心点的计算,用后面的那个等式呢?

这里简要解释一下视差的效果实现原理:

当第一张图片将要滚动的时候,第二章图片其实已经有一半在我们屏幕上.然后随着第一张图片移动,第一张图片移动一横屏,让第二章图片跟着移动半屏的距离就行了!

But!!真的是老赖上面说的那样吗?其实不是!不要轻易相信老赖!

之所以说不是,是因为假设第一张图片往右走,如果你让第二张图片也往右走,松开手后,效果完全不是这样.这里其实是让第二张图像左走.

对,相对于scrollView来说,第二章图片要向左走!为什么呢?因为整个scrollView都向右走,松手后,scrollView的可视位置,由中间变成了最左边,此时只有让第二章图片也出现在最左边也能完全展示出来它.第二张图片的初始位置是在一半屏幕宽度上的,所以它要往左走到零!

接着来说,图片加载相关.之前声明了如下的成员变量:

//记录当前图片下标
int _currentImageIndex;
//记录下一张图片下标
int _nextImageIndex;


.
当视图滚动后,直接可以通过下一张图片的下标从数据源里取图片

self.secondImageView.image = self.imageDataSource[_nextImageIndex];


.
然后,滚动,滚动,滚动,当图片是最后一张,再往后滚,或者当图片第一张,再往前滚的时候,就需要轮播,思想还是跳转:

if (_nextImageIndex == -1) {
_nextImageIndex = (int)self.imageDataSource.count - 1;
}else if (_nextImageIndex == self.imageDataSource.count){
_nextImageIndex = 0;
}


.
实现了图片的轮播加载后,可能有细心的好朋友们,一直疑问着呢…你scrollView只有三个屏幕的宽度,只能往左滚动一次,怎么实现轮播呢?

估计问这个问题的好朋友,没有细心看方法一,不错,这里也要让scrollView瞬间跳转!只不过,这里无论往左还是往右,scrollView跳转的位置都是最中心!也就是重新回到原来的位置.不过这里特别需要注意的是:在跳转回之前,一定要把中间位置上的第一张图片,设置成现在第二张图片,这样视觉上才不会有跳跃!

// 减速完成(分页滑动是会减速的)
- (void)scrollViewDidEndDecelerating:(UIscrollView *)scrollView
{
[self endRollscrollViewWith:scrollView];
}
// 滑动动画结束 setContentOffset: animated:YES 动画结束调用
- (void)scrollViewDidEndScrollingAnimation:(UIscrollView *)scrollView{
[self endRollscrollViewWith:scrollView];
}
//结束滚动后,重置页面
-(void)endRollscrollViewWith:(UIscrollView *)scrollView
{
//判断是否完成翻页
if (scrollView.contentOffset.x != CGRectGetWidth(self.frame)) {
//更新当前图片下标
_currentImageIndex = _nextImageIndex;
//给第一张相框添加图片
self.firstImageView.image = self.imageDataSource[_currentImageIndex];
//让视图瞬间回到中间位置
[self.scrollView setContentOffset:CGPointMake(CGRectGetWidth(self.frame), 0)];
//更新page
self.pageController.currentPage = _currentImageIndex;
}
}


.
看到这里,想必你大概也明白了这个视差轮播,以及只用两个UIImageView就能无限轮播的原理了吧!

那老赖也不卖关子了,把源码放上来吧:另外说一下,这里封装了两个,一个是代理协议回调的,一个是Block回调的,好朋友们各取所需吧:

https://github.com/wswei99/WSWscrollView

下面讲的是一些基本用法,demo里面的用例已经足够了,聪明的好朋友们,不用往下看了,帅的小伙伴们继续跟着老赖往下走吧!

先说说协议代理封装的:

@property (nonatomic, assign)  id<WSWscrollViewDelegate> delegate;
@property (nonatomic, assign)  id<WSWscrollViewDataSource> dataSource;
//@required
//给轮播视图提供数据源数组
- (NSArray *)imagesArrayForWSWscrollView:(WSWscrollView *)scrollView;
//@optional
//点击事件回调
- (void)wswScroView:(WSWscrollView *)scrollView didSelectRowAtIndexPath:(NSInteger)index;


.
使用这个轮播器,要遵循两个协议,并且必须实现一个方法:传入图片数组.

这里说明一下,图片数组不用你来区分本地还是网络图片了,老赖帮你判断了.

本地图片的话,你就传入照片名字的数组,别忘了jpg的要在名字后面加上.jpg哦!

网络图片的话,就传网络图片地址的字符串就行!

点击事件回调,会传过来一个图片的位置,你拿着这个位置下标,就可以进行其他操作了!

另外这里还有可调节时间计时器,手动销毁计时器,和手动开启计时器的外部接口,方便你自己添加功能或者项目需求吧;

再来说说Block封装的:

/**
*  传入照片,并返回图片点击的回调block
*
*  @param imagesArray            传入本地图片或者网络图片
*  @param currentImageClickBlock 图片点击的block方法
*/
- (void)addImagesArray:(NSArray *)imagesArray currentImageClick:(CurrentImageClick)currentImageClickBlock;


.
调用这个方法,传入图片源数组,然后手写回调block方法就可以了,下面是demo中的例子,可以看一眼:

//调用方法-->就是直接创建一个对象
WSWscrollView *scrollView = [[WSWscrollView alloc] initWithFrame:self.view.bounds];
/*
时间间隔一定要写在调用下面方法之前,因为方法里调用了创建时间控制器,
之后再设置间隔时间的话,就没有作用了
*/
scrollView.timeInterval = 1.f;
NSArray *array = @[@"火影01",@"火影02",@"火影03",@"火影04",];
//传入图片源数组并且实现回调block方法
[scrollView addImagesArray:array currentImageClick:^(NSInteger index) {
NSLog(@"--->我点的这是第%ld张图片",(long)index);
}];
//添加带父视图上
[self.view addSubview:scrollView];


.
哎呦,终于写完啦,iOS开发自学的课程还得继续更新,好了,老赖整理资料去了,收工!

封装好的Demo:https://github.com/wswei99/WSWScrollView
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: