ios实现无限循环滚动的两种方法,昨天已经讲了第一种,第一种方法的弊端是,假如要展示很多张图片(成百上千张),UIScrollView要设置的contentSize值会非常大,同时,假如这些图片都是从网络下载的,要先一次性下载全部的图片明显也不合理。因此一般采用第二种方法,只用三个图片视图用于展示,每次滑动结束后再获取新的图片数据,然后重新展示到视图上。
附:昨天写的第一种方法
ios之实现自动无限循环滚动视图(1)
其他部分和昨天的差不多,主要是在实现无限循环滚动时有区别。下面讲解一下原理。
UIScrollView的真实范围只有三个屏幕宽度,分成三个图片视图,每次滑动结束时,都要重新设置三个视图的图片(setImage)。设有一个图片数组imagesArray,每次只需要三张图片,即要展示的图片(i)、它前一张图片(i-1)、它后一张图片(i+1);当要展示的图片是第一张图片时,它前一张图片就是图片数组的最后一张图片imagesArray[lastObject];当要展示的图片是最后一张图片时,它后一张图片就是图片数组的第一张图片imagesArray[firstObject]。
因此需要一个变量来保存当前所展示的图片是第几张,并且实现一个方法,输入要展示的图片序号即可返回包含其左右图片的数组
方法的代码
//根据传来的值返回其左右的图像
-(NSMutableArray *)getImgToShowByIndex:(int )index{
NSMutableArray *imgArray = [NSMutableArray array];
if(index == 0){
//第一张 4 0 1
[imgArray addObject:[_imagesArray objectAtIndex:4]];
[imgArray addObject:[_imagesArray objectAtIndex:index]];
[imgArray addObject:[_imagesArray objectAtIndex:index +1]];
}else if(index == 4){
//最后一张 3 4 0
[imgArray addObject:[_imagesArray objectAtIndex:index - 1]];
[imgArray addObject:[_imagesArray objectAtIndex:index]];
[imgArray addObject:[_imagesArray objectAtIndex:0]];
}else{
[imgArray addObject:[_imagesArray objectAtIndex:index -1]];
[imgArray addObject:[_imagesArray objectAtIndex:index]];
[imgArray addObject:[_imagesArray objectAtIndex:index +1]];
}
return imgArray;
}
在滑动结束后,通过偏移量计算出移动的方向,然后计算出要展示的图片序号,调用方法得到用于展示的图片数组即可。需要注意,在滑动结束后仍需要偷偷将偏移量调整回中间视图的位置(不添加动画)。
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
//通过偏移量计算方向 等于零->向左 等于2->向右
//NSLog(@"%f",scrollView.contentOffset.x);
int direction = scrollView.contentOffset.x/KScreenWidth;
if (direction == 0) {
//向左
if (_index == 0) {
_index = 4;
}else{
_index --;
}
}else{
//向右
if (_index == 4) {
_index = 0;
}else{
_index ++;
}
}
_pageControl.currentPage = _index;
NSArray *array = [NSArray arrayWithArray:[self getImgToShowByIndex:_index]];
[_leftView setImage: [array objectAtIndex:0]];
[_middleView setImage:[array objectAtIndex:1]];
[_rightView setImage:[array objectAtIndex:2]];
//将偏移值移回中间
scrollView.contentOffset = CGPointMake(KScreenWidth, 0);
//两秒之后重新启动定时器
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(runLink) object:nil];
[self performSelector:@selector(runLink) withObject:nil afterDelay:2];
}
自动轮播这个部分,由于添加了pageControl,但是循环的实现主要靠数据的更新,而不是偏移量的改变。如果添加了动画会造成pageControl和图片变化不符的情况,没想好解决方法,就不添加动画效果,让它跳得丑一些……
在判断方向这一块没做好,出现了只要偏移量一发生视图一定会变化的情况。而实际情况是用户可能只拖了一点点之后不想滑了就放手了,此时不应该执行变化
这次的代码写得不是很好,有些部分感觉有点傻瓜。主要是想自己写一写感受一下原理,没去看其他人是怎么实现的,也没有进行什么优化。一开始想分享代码方便些就全部写在ViewController里了,为了快点做出效果数据也是死的,后来觉得这样非常不好,写过就过了,以后要用到还得再重新写。于是决定以后还是按照标准的方式,该封装就封装,便于以后复用,绝不重复写无意义的东西。
所以这个功能会用自定义类的方式再写一次,最近正在学着用GitHub,以后有意义一些的项目会直接分享到GitHub上。这个demo就到此为止了,存在的问题重写时会解决。
#import "ViewController.h"
#define KScreenWidth (self.view.frame.size.width)
@interface ViewController ()
@property(nonatomic,strong) UIScrollView *scrollView;
@property(nonatomic,strong) UIImageView *leftView;
@property(nonatomic,strong) UIImageView *middleView;
@property(nonatomic,strong) UIImageView *rightView;
@property(nonatomic,strong) UIPageControl *pageControl;
@property(nonatomic,strong) CADisplayLink *link;
//图片数组
@property(nonatomic,strong) NSMutableArray *imagesArray;
@property(nonatomic,assign) int index;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imagesArray = [NSMutableArray array];
//设置数据
for (int i = 0; i < 5; i ++) {
[_imagesArray addObject: [UIImage imageNamed:[NSString stringWithFormat:@"%d",i+1]]];
}
self.index = 0;
/*
只设置三张图片,每次滑动时重新加载数据(滑动完毕后将scrollView的偏移值移回中间位置
1 2 3 -> 滑动完毕 2 3 4
*/
//设置scrollView
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 100, KScreenWidth, 300)];
//scrollView的真实大小(三个屏幕即可
_scrollView.contentSize = CGSizeMake(3*KScreenWidth, 300);
//滑动到边缘时的弹簧效果
_scrollView.bounces = NO;
//是否分页显示
_scrollView.pagingEnabled = YES;
//设置背景颜色,便于观察
_scrollView.backgroundColor = [UIColor orangeColor];
//设置代理
_scrollView.delegate = self;
_scrollView.contentOffset = CGPointMake(KScreenWidth, 0);
//设置初始化页面
self.leftView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, 300)];
[self.leftView setImage:[_imagesArray objectAtIndex:4]];
[_scrollView addSubview:_leftView];
self.middleView = [[UIImageView alloc] initWithFrame:CGRectMake(KScreenWidth, 0, KScreenWidth, 300)];
[self.middleView setImage:[_imagesArray objectAtIndex:0]];
[_scrollView addSubview:_middleView];
self.rightView = [[UIImageView alloc] initWithFrame:CGRectMake(2*KScreenWidth, 0, KScreenWidth, 300)];
[self.rightView setImage:[_imagesArray objectAtIndex:1]];
[_scrollView addSubview:_rightView];
//设置小圆点
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(KScreenWidth/2-20, 350, 20, 20)];
_pageControl.numberOfPages = 5;
_pageControl.currentPage = 0;
_pageControl.pageIndicatorTintColor = [UIColor grayColor];
_pageControl.currentPageIndicatorTintColor = [UIColor orangeColor];
[self.view addSubview:_scrollView];
[self.view addSubview:_pageControl];
}
-(void)viewDidAppear:(BOOL)animated{
[self setupLink];
}
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
_link.paused = YES;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
//通过偏移量计算方向 等于零->向左 等于2->向右
//NSLog(@"%f",scrollView.contentOffset.x);
int direction = scrollView.contentOffset.x/KScreenWidth;
if (direction == 0) {
//向左
if (_index == 0) {
_index = 4;
}else{
_index --;
}
}else{
//向右
if (_index == 4) {
_index = 0;
}else{
_index ++;
}
}
_pageControl.currentPage = _index;
NSArray *array = [NSArray arrayWithArray:[self getImgToShowByIndex:_index]];
[_leftView setImage: [array objectAtIndex:0]];
[_middleView setImage:[array objectAtIndex:1]];
[_rightView setImage:[array objectAtIndex:2]];
//将偏移值移回中间
scrollView.contentOffset = CGPointMake(KScreenWidth, 0);
//两秒之后重新启动定时器
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(runLink) object:nil];
[self performSelector:@selector(runLink) withObject:nil afterDelay:2];
}
//根据传来的值返回其左右的图像
-(NSMutableArray *)getImgToShowByIndex:(int )index{
NSMutableArray *imgArray = [NSMutableArray array];
if(index == 0){
//第一张 4 0 1
[imgArray addObject:[_imagesArray objectAtIndex:4]];
[imgArray addObject:[_imagesArray objectAtIndex:index]];
[imgArray addObject:[_imagesArray objectAtIndex:index +1]];
}else if(index == 4){
//最后一张 3 4 0
[imgArray addObject:[_imagesArray objectAtIndex:index - 1]];
[imgArray addObject:[_imagesArray objectAtIndex:index]];
[imgArray addObject:[_imagesArray objectAtIndex:0]];
}else{
[imgArray addObject:[_imagesArray objectAtIndex:index -1]];
[imgArray addObject:[_imagesArray objectAtIndex:index]];
[imgArray addObject:[_imagesArray objectAtIndex:index +1]];
}
return imgArray;
}
#pragma ------------设置自动滚动--------------
-(void)setupLink{
//设定一个定时器
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(slideImage)];
_link.preferredFramesPerSecond = 1;
[_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
-(void)runLink{
_link.paused = NO;
}
-(void)slideImage{
//[_scrollView setContentOffset:CGPointMake(2*KScreenWidth, 0) animated:YES];
//自动滚动一直往右滚
if (_index == 4) {
_index = 0;
}else{
_index ++;
}
_pageControl.currentPage = _index;
NSArray *array = [NSArray arrayWithArray:[self getImgToShowByIndex:_index]];
[_leftView setImage: [array objectAtIndex:0]];
[_middleView setImage:[array objectAtIndex:1]];
[_rightView setImage:[array objectAtIndex:2]];
_scrollView.contentOffset = CGPointMake(KScreenWidth, 0);
}
@end