【iOS基础控件 - 7】 广告图片轮播器 <UIScrollView>
A.概念
例子就是桌面的APP列表,当APP数量超过一个屏幕,自动进行分页
B.实现思路
1.创建一个UIScrollView,这里设置为宽度跟屏幕相同,高度1/4屏幕高度左右
2.使用代码在UIScrollView中添加ImageView,横向放入多张ImageView
3.设置UIScrollView的contentSize为所有图片的宽度总和
4.要保证UIScrollView的宽度等于一张ImageView的宽度,才能正确分页
C.相关属性
设置属性pageEnable = YES,UIScrollView会被分割成多个独立页面,进行分页显示
一般使用UIPageControl增强效果,UIPageControl常见属性:
1 // 总页数 2 @property(nonatomic) NSInteger numberOfPages; // default is 0 3 // 当前页码 4 @property(nonatomic) NSInteger currentPage; 5 // 只有一页的时候隐藏页码 6 @property(nonatomic) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO 7 // 其他页码指示颜色 8 @property(nonatomic,retain) UIColor *pageIndicatorTintColor; 9 // 当前页码指示颜色 10 @property(nonatomic,retain) UIColor *currentPageIndicatorTintColor;
D.实现步骤
1. 创建一个UIScrollView,设置位置、尺寸
2.用代码加载多张图片
1 CGFloat imageWidth = 300; 2 CGFloat imageHeight = 130; 3 CGFloat imageY = 0; 4 5 for (int i=0; i<5; i++) { 6 CGFloat imageX = i * imageWidth; 7 UIImageView *currentImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"img_%02d", i+1]]]; 8 currentImageView.frame = CGRectMake(imageX, imageY, imageWidth, imageHeight); 9 10 [self.scrollView addSubview:currentImageView]; 11 }
3.设置contentSize
1 // 设置可以拖曳的范围,只允许横向拖曳,而且范围是所有的图片宽度总和 2 self.scrollView.contentSize = CGSizeMake(5 * imageWidth, 0);
4.开启 pageEnable = YES
每次拖曳至少滑动1个UIScrollView宽度单位,设置UIScrollView的宽度为图片的宽度之后,刚好翻过一页。
1 // 开启翻页模式,每次拖曳都会滑动一个UIScrollView宽度单位 2 self.scrollView.pagingEnabled = YES;
没有开启之前,切换两幅图片的时候终止拖曳,会停留在那一刻的显示状态
开启之后效果就像书本翻页一样
5.加入pageControl
pageControl要放在UIScrollView外,而且要比UIScrollView更前,才能正常显示
6.使用代码设置pageControl
pageControl属性:
1 // 总页数 2 @property(nonatomic) NSInteger numberOfPages; // default is 0 3 // 当前页码 4 @property(nonatomic) NSInteger currentPage; 5 // 只有一页的时候隐藏页码 6 @property(nonatomic) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO 7 // 其他页码指示颜色 8 @property(nonatomic,retain) UIColor *pageIndicatorTintColor; 9 // 当前页码指示颜色 10 @property(nonatomic,retain) UIColor *currentPageIndicatorTintColor;
(1)要手动设置pageControl,它才能正常工作
1 // 设置pageControl 2 self.pageControl.numberOfPages = 5; 3 self.pageControl.pageIndicatorTintColor = [UIColor blackColor]; 4 5 //设置delegate 6 self.scrollView.delegate = self;
(2)控制器遵守UIScrollViewDelegate协议,在 scrollViewDidScroll中动态设置当前页码
1 /** 当scrollView滚动的时候调用 */ 2 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 3 CGFloat scrollWidth = scrollView.frame.size.width; 4 5 // 当一副图片拖曳超过一半的时候就重新计算页码 6 int page = (scrollView.contentOffset.x + scrollWidth * 0.5) / scrollWidth; 7 8 self.pageControl.currentPage = page; 9 }
7.使用定时器NSTimer自动换页
(1)添加定时器
1 // 创建定时器 2 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];
(2)创建定时器调用的方法
1 - (void) nextImage { 2 // 1.增加pageControl的页码,这里不能直接操作currentPage,因为这样会和scrollViewDidScroll的页码计算冲突,应该是滚动画面,触发scrollViewDidScroll进行页码转换 3 int pageNo = 0; 4 if (self.pageControl.currentPage == (IMAGE_COUNT - 1)) { 5 pageNo = 0; 6 } 7 else { 8 pageNo = self.pageControl.currentPage + 1; 9 } 10 11 // 2.计算scrollView的滑动位置 12 CGFloat offsetX = pageNo * self.scrollView.frame.size.width; 13 CGPoint offset = CGPointMake(offsetX, 0); 14 15 // 移动一页,带动画效果 16 [self.scrollView setContentOffset:offset animated:YES]; 17 }
(3)定时器的弱点,在单线程运行时,得不到线程资源的时候,定时器定制运行,事件累积,得到资源之后才一起运行,这里有两种情况:
a.手动拖曳阻止了定时器
解决:销毁定时器,确保资源之后再创建一个
1 // 开始手动拖曳的时候,销毁定时器 2 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 3 [self.timer invalidate]; // 调用了invalidate之后再不可用了 4 self.timer = nil; 5 } 6 7 // 结束手动拖曳,重新定义定时器 8 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 9 [self addTimer]; 10 } 11 12 // 添加定时器 13 - (void) addTimer { 14 self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(nextImage) userInfo:nil repeats:YES]; 15 }
b.外部可刷新的控件抢占了所有线程资源
如下图中,添加了一个TextView,当拖曳其滚动条时,占用了所有线程刷新资源,定时器就被阻断了
解决:争取主线程资源来刷新(分享资源)
1 // 添加定时器 2 - (void) addTimer { 3 self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(nextImage) userInfo:nil repeats:YES]; 4 5 // 获得主线程资源,防止另外的如可滚动控件的资源全占用 6 [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; 7 }
E.滚动页的优化
1.只使用3个ImageView加载图片,动态加载图片
2.无限滚动
F.主要代码:
1 // 2 // ViewController.m 3 // ScrollViewPage 4 // 5 // Created by hellovoidworld on 14/11/28. 6 // Copyright (c) 2014年 hellovoidworld. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 11 #define IMAGE_COUNT 5 12 13 @interface ViewController () <UIScrollViewDelegate> 14 @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; // 滚动控件 15 @property (weak, nonatomic) IBOutlet UIPageControl *pageControl; // 页码控件 16 17 @property(nonatomic, strong) NSTimer *timer; // 定时器 18 19 @end 20 21 @implementation ViewController 22 23 - (void)viewDidLoad { 24 [super viewDidLoad]; 25 // Do any additional setup after loading the view, typically from a nib. 26 27 28 29 CGFloat imageWidth = 300; 30 CGFloat imageHeight = 130; 31 CGFloat imageY = 0; 32 33 for (int i=0; i<IMAGE_COUNT; i++) { 34 CGFloat imageX = i * imageWidth; 35 UIImageView *currentImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"img_%02d", i+1]]]; 36 currentImageView.frame = CGRectMake(imageX, imageY, imageWidth, imageHeight); 37 38 [self.scrollView addSubview:currentImageView]; 39 } 40 41 // 设置可以拖曳的范围,只允许横向拖曳,而且范围是所有的图片宽度总和 42 self.scrollView.contentSize = CGSizeMake(IMAGE_COUNT * imageWidth, 0); 43 44 // 开启翻页模式,每次拖曳都会滑动一个UIScrollView宽度单位 45 self.scrollView.pagingEnabled = YES; 46 47 // 设置pageControl 48 self.pageControl.numberOfPages = 5; 49 self.pageControl.pageIndicatorTintColor = [UIColor blackColor]; 50 self.pageControl.currentPageIndicatorTintColor = [UIColor redColor]; 51 52 // 设置delegate 53 self.scrollView.delegate = self; 54 55 // 创建定时器 56 [self addTimer]; 57 } 58 59 - (void)didReceiveMemoryWarning { 60 [super didReceiveMemoryWarning]; 61 // Dispose of any resources that can be recreated. 62 } 63 64 65 /** 当scrollView滚动的时候调用 */ 66 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 67 CGFloat scrollWidth = scrollView.frame.size.width; 68 69 // 当一副图片拖曳超过一半的时候就重新计算页码 70 int page = (scrollView.contentOffset.x + scrollWidth * 0.5) / scrollWidth; 71 72 self.pageControl.currentPage = page; 73 } 74 75 - (void) nextImage { 76 // 1.增加pageControl的页码,这里不能直接操作currentPage,因为这样会和scrollViewDidScroll的页码计算冲突,应该是滚动画面,触发scrollViewDidScroll进行页码转换 77 int pageNo = 0; 78 if (self.pageControl.currentPage == (IMAGE_COUNT - 1)) { 79 pageNo = 0; 80 } 81 else { 82 pageNo = self.pageControl.currentPage + 1; 83 } 84 85 // 2.计算scrollView的滑动位置 86 CGFloat offsetX = pageNo * self.scrollView.frame.size.width; 87 CGPoint offset = CGPointMake(offsetX, 0); 88 89 // 移动一页,带动画效果 90 [self.scrollView setContentOffset:offset animated:YES]; 91 } 92 93 // 开始手动拖曳的时候,销毁定时器 94 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 95 [self.timer invalidate]; // 调用了invalidate之后再不可用了 96 self.timer = nil; 97 } 98 99 // 结束手动拖曳,重新定义定时器 100 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 101 [self addTimer]; 102 } 103 104 // 添加定时器 105 - (void) addTimer { 106 self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(nextImage) userInfo:nil repeats:YES]; 107 108 // 获得主线程资源,防止另外的如可滚动控件的资源全占用 109 [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; 110 } 111 112 @end