博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ISO 平台 UIScrollView 用例 图像轮播器
阅读量:4200 次
发布时间:2019-05-26

本文共 6235 字,大约阅读时间需要 20 分钟。

一、使用到的基本控件

1、一般会配合UIPageControl增强分页效果,UIPageControl常用属性如下

(1)、一共有多少页
@property(nonatomic) NSInteger numberOfPages;
(2)、当前显示的页码
@property(nonatomic) NSInteger currentPage;
(3)、只有一页时,是否需要隐藏页码指示器
@property(nonatomic) BOOL hidesForSinglePage;ssssss
(4)、其他页码指示器的颜色
@property(nonatomic,retain) UIColor *pageIndicatorTintColor;
(5)、当前页码指示器的颜色
@property(nonatomic,retain) UIColor *currentPageIndicatorTintColor;

2、NSTimer叫做“定时器”,它的作用如下

在指定的时间执行指定的任务

每隔一段时间执行指定的任务

调用下面的方法就会开启一个定时任务

+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti   target:(id)aTarget    selector:(SEL)aSelector  userInfo:(id)userInfo  repeats:(BOOL)yesOrNo;

每隔ti秒,调用一次aTarget的aSelector方法,yesOrNo决定了是否重复执行这个任务

通过invalidate方法可以停止定时器的工作,一旦定时器被停止了,就不能再次执行任务。只能再创建一个新的定时器才能执行新的任务

- (void)invalidate;

二、实现步骤:

  1. 添加 UIScrollView

    创建一个UIScrollView, 设置宽为300, 高为130 (与每张图片的大小一致)

  2. 动态向 UIScrollView 中添加图片框(横向)

    向UIScrollView中添加内容(要滚动的内容, 添加到UIScrollView的子控件集合中)
    –循环添加5个UIImageView, 设置图片, 设置 frame

  3. 设置 UIScrollView 的 contentSize 实现滚动, 实现横向滚动

    设置UIScrollView的contentSize的width为5个图片的总大小, 上下不滚动所以height为0

  4. 实现分页

    只要将UIScrollView的pageEnabled属性设置为YES,UIScrollView会被分割成多个独立页面,里面的内容就能进行分页展示
    去掉水平滚动条
    –self.scrollView.showsHorizontalScrollIndicator = NO;

    问题: 设置完pagingEnabled = YES以后,scrollView是怎么知道该如何分页的?

    答: 按照UIScrollView自身的宽度来实现分页的.UIScrollView的宽度就是每页的大小。

  5. 实现分页指示器 UIPageControl

    通过UIPageControl来实现
    拽一个UIPageControl放到控制器的view中, 不要放到UIScrollView中, 否则就一起滚动了.
    设置UIPageControl的Tint Color(其他页颜色)和Current Page(当前页颜色)属性颜色
    注意: 当把UIPageControl添加到控制器的view中的时候, 这个控件和UIScrollView根本没有任何联系, 所以没有分页指示功能

  6. 通过使用 Nstimer 实现自动滚动

    在 viewDidLoad 中启动定时器
    启动定时器的两种方法:
    1> 调用timerWithXxx创建的timer,把这个timer对象手动加到”消息循环”中才能启动
    2> 调用scheduledTimerWithXxx创建的timer,自动启动(创建完毕后自动启动)。

实现分页指示器总页数、当前页。

–总页数: numberOfPages属性
self.pageControl.numberOfPages = imageCount;
–当前页: currentPage属性
•self.pageControl.currentPage = 0;
•注意:
–在 viewDidLoad 中设置总页数
–在- (void)scrollViewDidScroll:代理方法中设置当前页
–设置当前页的思路:
•通过当前的滚动的偏移值来计算出当前滚动到第几页了

在该方法中根据UIScrollView滚动到第几张图片,然后设置Page Control 当前第几个点显示为current page

当前页 = (当前滚动的偏移contentOffset.x + 半个图片的宽度) / 每个图片的宽度
** 问题: 为什么要用偏移加半个宽度?
设想:
如果contentOffset.x小于半个宽度,那么加上半个宽度以后除以图片宽度,结果还是0
如果contentOffset大于半个宽度,那么加上半个宽度以后除以一个宽度,结果还是1.
公式代码:
int page = (scrollView.contentOffset.x + 0.5 * scrollView.frame.size.width)/ scrollView.frame.size.width;
self.pageControl.currentPage = page;

//代理方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView{int page = (scrollView.contentOffset.x + 0.5 * scrollView.frame.size.width)/ scrollView.frame.size.width;self.pageControl.currentPage = page;}

三、处理细节的补充

1.两种不同的定时器

1> NSTimer(时间间隔比较大1秒,几秒)

2> CADisplayLink(时间间隔比较小,0.0几秒等)

在这里我们选择的是NSTimer定时器

创建、启动定时器代码参考:

// 方式一[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];// 方式二// 创建 NSTimer 对象NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test1) userInfo:nil repeats:YES];// 将刚创建的 NSTimer 对象加到消息循环中, 这样就会自动启动定时器NSRunLoop *runLoop = [NSRunLoop currentRunLoop];[runLoop addTimer:timer forMode:NSRunLoopCommonModes];// 方式三// 创建计时器对象NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test1) userInfo:nil repeats:YES]// 每次调用一次 fire 执行一次 test1方法[timer fire]; // 执行一次 test 方法[timer fire]; // 执行一次 test 方法[timer fire]; // 执行一次 test 方法[timer fire]; // 执行一次 test 方法

2. 在定时器的方法中实现滚动, 代码参考备注。

–思路1:

–1> 通过 UIPageControl 获取当前页数, 并让页数+1
–2> 根据加1以后的页数乘以每页的宽度(每张图片宽度)计算出contentOffset.x 的偏移值
–3> 手动设置偏移值, 实现滚动 (通过动画方式设置).

切换下一张图片的方法

在这个方法当中, 根据当前的页数, 来计算当前应该设置的偏移量contentOffset的值。

- (void)nextImage { // 获取当前的页数 NSInteger page = self.pageControl.currentPage; if (page == self.pageControl.numberOfPages - 1) {page = 0; } else {page++; } // 根据当前是第几页, 计算出偏移量contentOffset的值 CGFloat x = self.scrollView.frame.size.width * page; // 通过代码设置 contentOffset 偏移, 实现自动滚动 // 非动画方式 //self.scrollView.contentOffset = CGPointMake(x, 0); // 动画方式 [self.scrollView setContentOffset:CGPointMake(x, 0) animated:YES]; }

————————– 下面这种思路不好, 有时候有 bug —————————–

当手动拖拽滚动的时候, 此时 contentOffset.x 可能并不是一个完整的宽度, 所以会造成最终计算出来的 contentOffset.x 不是一个完整页的宽度

//直接获取现有的 offset.x 然后加上一个图片的宽度  CGFloat offsetX = self.scrollView.contentOffset.x;offsetX += self.scrollView.frame.size.width;if (offsetX >= self.scrollView.contentSize.width) {offsetX = 0;}//self.scrollView.contentOffset = CGPointMake(offsetX, 0);[self.scrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];

3. 解决UIScrollView处理Bug

–Bug: 当拖拽UIScrollView的时候, 保持一段时间不松手的时候, 一旦松手UIScrollView会连续滚动多次。

–解决思路:在即将拖拽的时候, 停止计时器, 拖拽完毕后再打开一个计时器。

停止计时器:调用 NSTimer 对象的 invalidate 方法(当某个计时器被停止以后, 就无法再重用了, 下次必须再重新创建一个新的计时器)。 [self.timer invalidate];

  • (void)scrollViewWillBeginDragging:
  • (void)scrollViewDidEndDragging:
参考代码:
// 在即将拖拽之前 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { // 停止计时器 [self.timer invalidate]; self.timer = nil; } // 在拖拽完毕后再启动计时器 -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {  // 重新创建一个计时器对象self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];// 获取当前线程处理事件的消息循环NSRunLoop *runLoop = [NSRunLoop currentRunLoop];// 设置timer的优先级[runLoop addTimer:timer forMode:NSRunLoopCommonModes]; }

4. 解决 NSTimer 运行优先级的 Bug

Bug:当单击(拖拽)界面上的某个其他控件的时候, UIScrollView停止滚动的问题。

•产生 Bug 的原因:
–当前处理UI界面的只有一个线程, 当这个线程处理UI的拖动事件的时候就没有能力再去处理滚动操作了
–注意: 处理UI界面的的只能是一个线程。所以, 处理UIScrollView的滚动和其他控件的拖拽, 只能用同一个线程。如果多个线程都可以操作 UI 那么就会造成混乱的问题
•解决思路: 提高处理滚动的timer的优先级。
•注意: 所有控件的默认优先级都是NSRunLoopCommonModes ,但是网络和计时器对象默认的优先级要比控件的优先级低是 NSDefaultRunLoopMode , 所以这里要把计时器的优先级调整为与控件一样的优先级NSRunLoopCommonModes。

解决
  • 提高处理滚动的timer的优先级。
  • 每次创建完毕timer控件的时候, 都设置一下timer的优先级
  • 在 viewDidLoad 中第一次创建完毕 timer 的时候修改一次优先级
#参考代码:
// 获取当前线程处理事件的消息循环NSRunLoop *runLoop = [NSRunLoop currentRunLoop];// 设置timer的优先级[runLoop addTimer:timer forMode:NSRunLoopCommonModes];// 2. 在scrollViewDidEndDragging:中重新创建timer 对象后再修改一次优先级-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {  // 重新创建一个计时器对象self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];// 获取当前线程处理事件的消息循环NSRunLoop *runLoop = [NSRunLoop currentRunLoop];// 设置timer的优先级[runLoop addTimer:timer forMode:NSRunLoopCommonModes]; }

程序效果

这里写图片描述

这里写图片描述

你可能感兴趣的文章
MySQL数据库之基础备份
查看>>
http协议之常用状态码总结
查看>>
python中的四个BIF:filter()、map()、zip()、enumerate()
查看>>
python 变量作用域、闭包、装饰器
查看>>
python中的浅拷贝,深拷贝的区别和理解
查看>>
python之迭代器(Iterator)、生成器(yield)、协程(gevent)
查看>>
python之字符串(str)和编码
查看>>
数据结构与算法-七种排序算法
查看>>
python之sklearn-特征工程-1.1特征工程
查看>>
python之sklearn-特征工程-1.2 特征抽取
查看>>
python之sklearn-特征工程-1.3 特征预处理
查看>>
python之sklearn-特征工程-1.4 特征选择
查看>>
python之sklearn-特征工程-1.5 特征降维
查看>>
python之sklearn-特征工程-1.6 机器学习算法简介
查看>>
python之sklearn- 分类算法-2.1 数据集介绍与划分
查看>>
python之sklearn- 分类算法-2.2 sklearn转换器和估计器
查看>>
python之sklearn-分类算法-2.5 朴素贝叶斯算法
查看>>
python之sklearn-分类算法-2.6 决策树
查看>>
python之sklearn-分类算法-3.1 线性回归
查看>>
python之sklearn-分类算法-3.2 欠拟合与过拟合
查看>>