iOS --- 轮播图
在iOS开发中,有很多项目使用到了轮播图,通常我们更多的是使用一些别人写的方法来实现,当然这样也更加快捷和方便,但其中的实现原理却一窍不通,最近对轮播图研究了一番,也试着去实现了一些基本的功能,下面就让我来介绍一下:
本文中的轮播图主要是实用UIScrollView + NSTimer来实现的,具体思路是:首先我们先创建一个UIScrollView,根据图片的数据设置UIScrollView的内容尺寸(因为要实现无限轮播的的效果,需要多设置一个宽度的内容尺寸来起到缓冲的作用,使其显示到最后一张时,当我们再次滑动,直接跳转到第一张,即UIScrollView的内容尺寸为图片数据总和再加1,下面会详细介绍),然后创建一个UIPageControl来现实图片的具体位置,并让其与UIScrollView起到联动的效果。最后创建一个定时器,每隔一段时间设置UIScrollView的偏移量,注意也要更改UIPageControl的选中页位置。当然这种方法主要适用于数据较少的时候使用,当数据较多时,我们就要考虑复用的问题了。
首先确定数据源,这里我们使用本地数据,创建图片数组来存放五张图片
1 _imgArray = @[@"01.jpg", @"02.jpg", @"03.jpg", @"04.jpg", @"05.jpg"];
创建UIScrollView和UIPageControl,注意UIScrollView的内容尺寸为_imgArray.count + 1,UIScrollView的最后一个位置要放置地一张图片
1 _scrollView = [[UIScrollView alloc]initWithFrame:CGRctMake(0, 0, kScreenWidth, 200)]; 2 _scrollView.contentSize = CGSizeMake(kScreenWidth * (_imgArray.count + 1), 200); 3 _scrollView.pagingEnabled = YES; 4 _scrollView.delegate = self; 5 _scrollView.showsHorizontalScrollIndicator = NO; 6 //创建子视图 7 for (int i = 0; i < _imgArray.count + 1; i++) { 8 UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(0 + i * kScreenWidth, 0, kScreenWidth, 200)]; 9 if (i == _imgArray.count) { 10 imgView.image = [UIImage imageNamed:_imgArray[0]]; 11 }else{ 12 imgView.image = [UIImage imageNamed:_imgArray[i]]; 13 } 14 [_scrollView addSubview:imgView]; 15 } 16 [self.view addSubview:_scrollView]; 17 18 //创建分页控件 19 _pageCtrl = [[UIPageControl alloc]initWithFrame:CGRectMake((kScreenWidth - 130)/2, _scrollView.bottom - 20, 130, 15)]; 20 //_pageCtrl.backgroundColor = [UIColor cyanColor]; 21 _pageCtrl.numberOfPages = _imgArray.count; 22 _pageCtrl.currentPage = 0; 23 [self.view addSubview:_pageCtrl];
实现UIScrollView的代理方法 - (void)scrollViewDidScroll:(UIScrollView *)scrollView,在此方法中可以获取到scrollView的偏移量,根据偏移量获取分页位置。假设屏幕宽度为320,根据上图可明显得出,当向右滑动xOff为1600时,UIScrollView恰好滑到了最后一个索引处,即第一张图片。此时如果我们不添加处理,继续向右滑动,没有图片,这不是我们想要的结果。无限循环滑动才是我们的目的,此时,我们想看到的是第二张图片,索引为1。因此,当xOff大于1600 的瞬间,设置UIScrollView的偏移量为CGPointMake(0, 0),此时我们看到的仍然是第一张图片,但是已经实现了循环跳转,只是我们肉眼难以察觉。注意,设置偏移量时animate要设置为NO。同理,当向左滑动xOff为0时,继续滑动,我们想要看到的是第五张图片,索引为4。因此当xOff小于0时,设置设置UIScrollView的偏移量为CGPointMake(1600, 0),此时我们看到的仍然是第一张图片(第六张图片,索引5)。注意,在此处处理UIPageControl的选中页位置。
1 - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 2 CGFloat xOff = scrollView.contentOffset.x; 3 int index = xOff/kScreenWidth; 4 5 if (index == 5) { 6 _pageCtrl.currentPage = 0; 7 }else{ 8 _pageCtrl.currentPage = index; 9 } 10 // 0 1 2 3 4 0 11 // 0 320 640 960 1280 1600 12 if (xOff > kScreenWidth * _imgArray.count) { 13 14 [scrollView setContentOffset:CGPointMake(0, 0) animated:NO]; 15 16 } 17 if (xOff < 0) { 18 [scrollView setContentOffset:CGPointMake(kScreenWidth * _imgArray.count, 0) animated:NO]; 19 } 20 21 }
至此,轮播图的基本工功能,我们已经大致实现了。下面,我们添加一个定时器,使UIScrollView能够根据定时器每隔1s自动滚动:
number = 0; _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(setOffsetMethod) userInfo:nil repeats:YES]; - (void)setOffsetMethod{ number++; [_scrollView setContentOffset:CGPointMake(kScreenWidth * number, 0) animated:YES]; }
要注意当滚动到最后一个索引时,使number= 0;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ CGFloat xOff = scrollView.contentOffset.x; int index = xOff/kScreenWidth; if (index == 5) { _pageCtrl.currentPage = 0; }else{ _pageCtrl.currentPage = index; } // 0 1 2 3 4 0 // 0 320 640 960 1280 1600 if (xOff > kScreenWidth * _imgArray.count) { number = 0; [scrollView setContentOffset:CGPointMake(0, 0) animated:NO]; } if (xOff < 0) { [scrollView setContentOffset:CGPointMake(kScreenWidth * _imgArray.count, 0) animated:NO]; } }
接下来有一个重要问题就是解决定时器与滑动手势的冲突,实现下面两个代理方法:
//开始拖动 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ NSLog(@"开始拖动"); [_timer setFireDate:[NSDate distantFuture]]; } //结束拖动 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ NSLog(@"结束拖动"); int index = scrollView.contentOffset.x/kScreenWidth; number = index; [_timer setFireDate:[NSDate date]]; }
下面是轮播图项目的全部代码:
1 // 轮播图 2 3 #import "Test02ViewController.h" 4 #import "UIViewExt.h" 5 #import "Test01ViewController.h" 6 7 @interface Test02ViewController ()<UIScrollViewDelegate> 8 { 9 NSInteger number; 10 } 11 @property(nonatomic, strong)UIScrollView *scrollView; 12 @property(nonatomic, strong)NSArray *imgArray; 13 @property(nonatomic, strong)UIPageControl *pageCtrl; 14 15 @property(nonatomic, strong)NSTimer *timer; 16 17 @end 18 19 @implementation Test02ViewController 20 21 - (void)viewDidLoad { 22 [super viewDidLoad]; 23 // Do any additional setup after loading the view. 24 self.view.backgroundColor = [UIColor whiteColor]; 25 self.navigationController.navigationBar.translucent = NO; 26 _imgArray = @[@"01.jpg", @"02.jpg", @"03.jpg", @"04.jpg", @"05.jpg"]; 27 [self addSubViews]; 28 NSLog(@"viewDidLoad"); 29 } 30 31 - (void)viewWillAppear:(BOOL)animated{ 32 [super viewWillAppear:animated]; 33 [_scrollView setContentOffset:CGPointMake(0, 0) animated:NO]; 34 35 } 36 - (void)viewDidAppear:(BOOL)animated{ 37 [super viewDidAppear:animated]; 38 39 number = 0; 40 _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(setOffsetMethod) userInfo:nil repeats:YES]; 41 } 42 - (void)viewWillDisappear:(BOOL)animated{ 43 [super viewWillDisappear:animated]; 44 45 [_timer invalidate]; 46 } 47 48 - (void)addSubViews{ 49 _scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 200)]; 50 _scrollView.contentSize = CGSizeMake(kScreenWidth * (_imgArray.count + 1), 200); 51 _scrollView.pagingEnabled = YES; 52 _scrollView.delegate = self; 53 _scrollView.showsHorizontalScrollIndicator = NO; 54 //创建子视图 55 for (int i = 0; i < _imgArray.count + 1; i++) { 56 UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(0 + i * kScreenWidth, 0, kScreenWidth, 200)]; 57 if (i == _imgArray.count) { 58 imgView.image = [UIImage imageNamed:_imgArray[0]]; 59 }else{ 60 imgView.image = [UIImage imageNamed:_imgArray[i]]; 61 } 62 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init]; 63 [tap addTarget:self action:@selector(touchAction)]; 64 [imgView addGestureRecognizer:tap]; 65 imgView.userInteractionEnabled = YES; 66 67 [_scrollView addSubview:imgView]; 68 } 69 [self.view addSubview:_scrollView]; 70 71 //创建分页控件 72 _pageCtrl = [[UIPageControl alloc]initWithFrame:CGRectMake((kScreenWidth - 130)/2, _scrollView.bottom - 20, 130, 15)]; 73 //_pageCtrl.backgroundColor = [UIColor cyanColor]; 74 _pageCtrl.numberOfPages = _imgArray.count; 75 _pageCtrl.currentPage = 0; 76 [self.view addSubview:_pageCtrl]; 77 78 } 79 - (void)touchAction{ 80 NSLog(@"点击"); 81 [_timer setFireDate:[NSDate distantFuture]]; 82 [self pushVC]; 83 } 84 85 #pragma mark -UIScrollViewDelegate 86 //已经滑动 87 - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 88 CGFloat xOff = scrollView.contentOffset.x; 89 int index = xOff/kScreenWidth; 90 91 if (index == 5) { 92 _pageCtrl.currentPage = 0; 93 }else{ 94 _pageCtrl.currentPage = index; 95 } 96 // 0 1 2 3 4 0 97 // 0 320 640 960 1280 1600 98 if (xOff > kScreenWidth * _imgArray.count) { 99 number = 0; 100 [scrollView setContentOffset:CGPointMake(0, 0) animated:NO]; 101 102 } 103 if (xOff < 0) { 104 [scrollView setContentOffset:CGPointMake(kScreenWidth * _imgArray.count, 0) animated:NO]; 105 } 106 107 } 108 - (void)setOffsetMethod{ 109 110 number++; 111 [_scrollView setContentOffset:CGPointMake(kScreenWidth * number, 0) animated:YES]; 112 113 } 114 //开始拖动 115 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ 116 NSLog(@"开始拖动"); 117 [_timer setFireDate:[NSDate distantFuture]]; 118 } 119 //结束拖动 120 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ 121 NSLog(@"结束拖动"); 122 int index = scrollView.contentOffset.x/kScreenWidth; 123 number = index; 124 [_timer setFireDate:[NSDate date]]; 125 } 126 - (void)pushVC{ 127 Test01ViewController *test01VC = [[Test01ViewController alloc]init]; 128 [self.navigationController pushViewController:test01VC animated:YES]; 129 } 130 131 132 133 - (void)didReceiveMemoryWarning { 134 [super didReceiveMemoryWarning]; 135 // Dispose of any resources that can be recreated. 136 } 137 138 /* 139 #pragma mark - Navigation 140 141 // In a storyboard-based application, you will often want to do a little preparation before navigation 142 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 143 // Get the new view controller using [segue destinationViewController]. 144 // Pass the selected object to the new view controller. 145 } 146 */ 147 148 @end