iOS-實現UIScrollview的無限循環滑動(轉載自Kenshin Cui's Blog)不過感覺快速滑動還是有缺陷啊

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張圖片顯示的情景):

loopScroll2

如何優化性能?

無限循環實現了,但是我們知道如果圖片過多這些圖片勢必全部加載到內存,這是我們不願意看到的,此時我們需要優化上面的方案。其實從上面的方案我們也可以看出端倪,我們完全沒必要創建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

在上面的代碼中需要提醒大家的是一定要謹慎在滾動時進行相關操作,前面我們說過滾動事件會循環執行十分消耗性能,因此如果能不在其中操作的話儘可能不要在這個方法中進行相關操作,例如在上面的代碼中我們的核心邏輯主要集中在滾動停止事件中,這個事件在一次滾動操作中只需要執行一次。

原文地址:點擊打開鏈接

運行效果:

loopScrollTuning

發佈了84 篇原創文章 · 獲贊 107 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章