UIScrollView实战
前面介绍了iOS中UIKit的一些简单知识,这里我们一起利用前面的知识做一个例子--图片无限循环滚动。在这个例子中我们需要解决如下两个问题:
如何无限循环?
我们知道在UIScrollView中如果放置其他控件后,只要设置contentSize之后这些图片就可以滚动。如果要让图片无限循环那么只有两种办法,一种是无限循环叠加图片,另一种就是如果最后一张图片浏览完立即显示第一张图片。很明显第一种方法是不现实的,我们考虑使用第二种方式。其实使用第二种方式实现原理比较简单,只要在图片前后各放一张图片即可(此时共有n+2个图片在UIScrollView中)。例如我们有5张图片,只要使用7个UIImageView依次存放:图片5,图片1,图片2,图片3,图片4,图片5,图片1。当从图片1滚动到图片5时由于最后一张是图片1就给用户一种无限循环的感觉,当这张图完全显示后我们迅速将UIScrollView的contentOffset设置到第二个UIImageView,也就是图片1,接着用户可以继续向后滚动。当然向前滚动原理完全一样,当滚动到第一张图片(图片5)就迅速设置UIScrollView的contentOffset显示第6张图(图片5)。为了方便说明请看下面的示意图(注意示意图由于宽度有限只描述了3张图片显示的情景):
如何优化性能?
无限循环实现了,但是我们知道如果图片过多这些图片势必全部加载到内存,这是我们不愿意看到的,此时我们需要优化上面的方案。其实从上面的方案我们也可以看出端倪,我们完全没必要创建n+2个UIImageView,其实3个已经足够(事实上也可以用两个实现,大家不妨自己思考一下),只要一直保持显示中间的UIImageView,滚动时动态更改三个UIImageView的图片即可。例如三个UIImageView默认放图片5、图片1、图片2,当前显示中间的UIImageView,也就是图片1,。如果向后滚动那么就会显示图片2,当图片2显示完整后迅速重新设置三个UIImageView的内容为图片1、图片2、图片3,然后通过contentOffset设置显示中间的UIImageView,也就是图片2。继续向后看到图片3,当图片3滚动完成迅速重新设置3个UIImageView的内容为图片2、图片3、图片4,然后设置contentOffset显示中间的UIImageView,也就是图片3。当然,向前滚动原理完全一样,如此就给用户一种循环错觉,而且不占用过多内存。
下面给出具体的实现,在这个程序中除了UIscrollView我们还可以看到UIPageControl的使用,实现并不复杂。首先我们需要将图片信息存储到plist文件中(日后方便扩展),并且设置plist的key表示图片的名称,value代表对应的图片描述,这个描述我们需要展示在界面上方。具体内容如下:
imageInfo.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>0.jpg</key> <string>iphone 5s</string> <key>1.jpg</key> <string>iphone 5c</string> <key>2.jpg</key> <string>ipad min with retain</string> <key>3.jpg</key> <string>ipad air</string> <key>4.jpg</key> <string>ipod</string> <key>5.jpg</key> <string>ipod touch</string> <key>6.jpg</key> <string>mac book pro</string> <key>7.jpg</key> <string>mac book air</string> <key>8.jpg</key> <string>imac</string> </dict> </plist>
在程序中我们需要读取plist文件并加载对应的图片,这里我们将图片按顺序依次命名:0.jpg、1.jpg…8.jpg。我们的程序主要集中于自定义的KCMainViewController.m中,在这里我们声明1个UIScrollView和3个UIImageView用于显示图片,同时声明一个UILable显示图片描述信息,声明一个UIPageControl来显示当前图片页数,具体代码如下:
// // KCMainViewController.m // ImageViewer // // Created by Kenshin Cui on 14-2-23. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 568 #define IMAGEVIEW_COUNT 3 @interface KCMainViewController ()<UIScrollViewDelegate>{ UIScrollView *_scrollView; UIImageView *_leftImageView; UIImageView *_centerImageView; UIImageView *_rightImageView; UIPageControl *_pageControl; UILabel *_label; NSMutableDictionary *_imageData;//图片数据 int _currentImageIndex;//当前图片索引 int _imageCount;//图片总数 } @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; //加载数据 [self loadImageData]; //添加滚动控件 [self addScrollView]; //添加图片控件 [self addImageViews]; //添加分页控件 [self addPageControl]; //添加图片信息描述控件 [self addLabel]; //加载默认图片 [self setDefaultImage]; } #pragma mark 加载图片数据 -(void)loadImageData{ //读取程序包路径中的资源文件 NSString *path=[[NSBundle mainBundle] pathForResource:@"imageInfo" ofType:@"plist"]; _imageData=[NSMutableDictionary dictionaryWithContentsOfFile:path]; _imageCount=(int)_imageData.count; } #pragma mark 添加控件 -(void)addScrollView{ _scrollView=[[UIScrollView alloc]initWithFrame:[UIScreen mainScreen].bounds]; [self.view addSubview:_scrollView]; //设置代理 _scrollView.delegate=self; //设置contentSize _scrollView.contentSize=CGSizeMake(IMAGEVIEW_COUNT*SCREEN_WIDTH, SCREEN_HEIGHT) ; //设置当前显示的位置为中间图片 [_scrollView setContentOffset:CGPointMake(SCREEN_WIDTH, 0) animated:NO]; //设置分页 _scrollView.pagingEnabled=YES; //去掉滚动条 _scrollView.showsHorizontalScrollIndicator=NO; } #pragma mark 添加图片三个控件 -(void)addImageViews{ _leftImageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]; _leftImageView.contentMode=UIViewContentModeScaleAspectFit; [_scrollView addSubview:_leftImageView]; _centerImageView=[[UIImageView alloc]initWithFrame:CGRectMake(SCREEN_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]; _centerImageView.contentMode=UIViewContentModeScaleAspectFit; [_scrollView addSubview:_centerImageView]; _rightImageView=[[UIImageView alloc]initWithFrame:CGRectMake(2*SCREEN_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]; _rightImageView.contentMode=UIViewContentModeScaleAspectFit; [_scrollView addSubview:_rightImageView]; } #pragma mark 设置默认显示图片 -(void)setDefaultImage{ //加载默认图片 _leftImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"%i.jpg",_imageCount-1]]; _centerImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"%i.jpg",0]]; _rightImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"%i.jpg",1]]; _currentImageIndex=0; //设置当前页 _pageControl.currentPage=_currentImageIndex; NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentImageIndex]; _label.text=_imageData[imageName]; } #pragma mark 添加分页控件 -(void)addPageControl{ _pageControl=[[UIPageControl alloc]init]; //注意此方法可以根据页数返回UIPageControl合适的大小 CGSize size= [_pageControl sizeForNumberOfPages:_imageCount]; _pageControl.bounds=CGRectMake(0, 0, size.width, size.height); _pageControl.center=CGPointMake(SCREEN_WIDTH/2, SCREEN_HEIGHT-100); //设置颜色 _pageControl.pageIndicatorTintColor=[UIColor colorWithRed:193/255.0 green:219/255.0 blue:249/255.0 alpha:1]; //设置当前页颜色 _pageControl.currentPageIndicatorTintColor=[UIColor colorWithRed:0 green:150/255.0 blue:1 alpha:1]; //设置总页数 _pageControl.numberOfPages=_imageCount; [self.view addSubview:_pageControl]; } #pragma mark 添加信息描述控件 -(void)addLabel{ _label=[[UILabel alloc]initWithFrame:CGRectMake(0, 10, SCREEN_WIDTH,30)]; _label.textAlignment=NSTextAlignmentCenter; _label.textColor=[UIColor colorWithRed:0 green:150/255.0 blue:1 alpha:1]; [self.view addSubview:_label]; } #pragma mark 滚动停止事件 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ //重新加载图片 [self reloadImage]; //移动到中间 [_scrollView setContentOffset:CGPointMake(SCREEN_WIDTH, 0) animated:NO]; //设置分页 _pageControl.currentPage=_currentImageIndex; //设置描述 NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentImageIndex]; _label.text=_imageData[imageName]; } #pragma mark 重新加载图片 -(void)reloadImage{ int leftImageIndex,rightImageIndex; CGPoint offset=[_scrollView contentOffset]; if (offset.x>SCREEN_WIDTH) { //向右滑动 _currentImageIndex=(_currentImageIndex+1)%_imageCount; }else if(offset.x<SCREEN_WIDTH){ //向左滑动 _currentImageIndex=(_currentImageIndex+_imageCount-1)%_imageCount; } //UIImageView *centerImageView=(UIImageView *)[_scrollView viewWithTag:2]; _centerImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"%i.jpg",_currentImageIndex]]; //重新设置左右图片 leftImageIndex=(_currentImageIndex+_imageCount-1)%_imageCount; rightImageIndex=(_currentImageIndex+1)%_imageCount; _leftImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"%i.jpg",leftImageIndex]]; _rightImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"%i.jpg",rightImageIndex]]; } @end
在上面的代码中需要提醒大家的是一定要谨慎在滚动时进行相关操作,前面我们说过滚动事件会循环执行十分消耗性能,因此如果能不在其中操作的话尽可能不要在这个方法中进行相关操作,例如在上面的代码中我们的核心逻辑主要集中在滚动停止事件中,这个事件在一次滚动操作中只需要执行一次。
原文地址:点击打开链接
运行效果: