• 问题总会出现,不过解决问题的方法也会出现!!!

UIScrollView常用属性、代理方法

UIScrollView 小雨 725次浏览 已收录 0个评论

UIScrollView的常见属性
UIScrollView 实现滚动、缩放
UIScrollView的常用代理方法
UIScrollView和UIPageControl的分页
• NSTimer的使用, 计时器控件

UIScrollView的基本介绍

UIScrollView 是干什么的?

UIScrollView 也是一种控件
用来实现”滚动”和”缩放”的控件

为什么要进行”滚动”或”缩放”?

移动设备屏幕大小有限, 当要显示的内容太大时, 为了能更方便的查看内容所以需要”滚动”和”缩放”

谁进行“滚动”或”缩放”?

“滚动”和”缩放”指的是针对放到 UIScrollView 控件里面的内容(可以是图片、表格、其他控件等), UIScrollView 控件本身不动

举例:

1.手机上的“设置”如:设置-> 隐私,设置 -> Safari
2.大图滚动显示案例
3.图片缩放案例

UIScrollView的基本使用

1. 拖一个 UIScrollView 到界面上
2. 将需要展示的内容添加到UIScrollView中
3. 设置UIScrollView的contentSize属性

contentSize 属性的含义是: 告诉UIScrollView 要展示的内容实际有多大(尺寸),也就是告诉UIScrollView滚动的范围(能滚多远,滚到哪里是尽头)

UIScrollView无法滚动可能的原因及解决办法

如果UIScrollView无法滚动,可能是以下原因:
没有设置contentSize
scrollEnabled = NO
没有接收到触摸事件:userInteractionEnabled = NO
没有取消autolayout功能(要想scrollView滚动,在 xcode5.x 下必须取消autolayout)注意: 在xcode6.1下不需要取消自动布局也可以滚动。UIScrollView常用属性、代理方法

注意

UIScrollView的 frame.size 与 contentSize 的区别?

frame.size指的是: UIScrollView的可视区域的大小, UIScrollView本身的大小
contentSize指的是: UIScrollView中所包含的内容的大小(要滚动的实际内容的大小)

提问:

把frame.size 和 contentSize设置都一样了, 还能滚动吗?如果 contentSize 比 frame.size 小还能滚动吗?

总结:

在UIScrollView的frame.size这么大的范围内, 要显示contentSize这么大的内容。

是否需要滚动, 取决于contentSize是否比frame.size大。

UIScrollView的常见属性: contentOffset

@property(nonatomic) CGPoint contentOffset;

属性含义:

1> 当UIScrollView内部的内容滚动时, 内容相对于UIScrollView左上角的偏移
2> 另一种理解方式: 内容滚动到了什么位置

UIScrollView常用属性、代理方法

案例

通过点击按钮, 实现大图自动滚动(修改 contentOffset)。

UIScrollView常用属性、代理方法

步骤

1. 直接实现, 直接修改 contentOffset 没有动画效果
2. 通过UIView的动画方法实现
3. 通过调用UIScrollView的setContentOffset:animated:来实现带动画效果的滚动。

UIScrollView的常见属性contentInset

@property(nonatomic) UIEdgeInsets contentInset;

含义:

内容的内边距
设置UIScrollView的内容在拖动以后, 内容距离UIScrollView的内边距。(联想按钮的内边距属性: Inset)
另一种思考方式: 想象成把内容加大了, 在内容本身的周围加了一圈”外边距”。

案例

演示设置了 contentInset 属性后的效果。UIScrollView常用属性、代理方法

蓝色区域为模拟器所显示的屏幕范围UIScrollView常用属性、代理方法

UIScrollView的其他属性

@property(nonatomic) BOOL bounces;
设置UIScrollView是否需要弹簧效果
@property(nonatomic,getter=isScrollEnabled) BOOL scrollEnabled;
设置UIScrollView是否能滚动
@property(nonatomic) BOOL showsHorizontalScrollIndicator;
是否显示水平滚动条
@property(nonatomic) BOOL showsVerticalScrollIndicator;
是否显示垂直滚动条

提示: 某个方向上不希望滚动, 则把该方向上的contentSize的值设置为0

实现滚动的时候输出当前滚动的位置

思考:

0.获取当前滚动的位置:scrollView.contentOffset
1.要想实现滚动的时候显示当前滚动的位置, 那么就一定要监听滚动事件。
2.问题:如何监听滚动事件?答:通过代理来监听。
监听事件的另外一种方式: 代理。之前学过的一种方式是:addTarget 的方式。

UIScrollView的代理对象是如何工作的?

UIScrollView常用属性、代理方法在OC中,发送消息的意思就是调用方法
因此UIScrollView和delegate的通信可以理解为下图所示UIScrollView常用属性、代理方法

再精确一点,UIScrollView和delegate的通信应该为下图所示UIScrollView常用属性、代理方法

也就是说, 只要设置好了 UIScrollView的代理对象, 那么当某个事件被触发以后, 系统会自动调用代理对象的相应方法。
我们要关心的就是如何设置代理, 其他细节无需关心。

成为delegate的条件

UIScrollView将delegate需要实现的方法都定义在了UIScrollViewDelegate协议中,因此要想成为UIScrollView的delegate,必须遵守UIScrollViewDelegate协议,然后实现协议中相应的方法,就可以监听UIScrollView的滚动过程了

UIScrollView常用属性、代理方法

某个对象成为UIScrollView 的delegate的条件

1. 设置 UIScrollView 的代理为这个对象
2. 让这个对象遵守UIScrollViewDelegate代理协议
3. 让这个代理对象实现代理协议中的代理方法

UIScrollViewDelegate协议

// 用户开始拖拽时调用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
// 滚动到某个位置时调用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
// 用户结束拖拽时调用
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

UIScrollView代理属性
id delegate

任意类型的对象 遵守协议,实现相应的方法

UIScrollView的代理(delegate)

很多时候,我们想在UIScrollView正在滚动 或 滚动到某个位置 或者 停止滚动 时做一些特定的操作
要想完成上述功能,前提条件就是能够监听到UIScrollView的整个滚动过程
当UIScrollView发生一系列的滚动操作时, 会自动通知它的代理(delegate)对象,给它的代理发送相应的消息,让代理得知它的滚动情况
也就是说,要想监听UIScrollView的滚动过程,就必须先给UIScrollView设置一个代理对象,然后通过代理得知UIScrollView的滚动过程

将当前控制器设置为 UIScrollView 的代理

1. 设置UIScrollView 的代理为当前控制器
self.scrollView.delegate = self;
2. 让当前控制器遵守 UIScrollViewDelegate 协议
3. 在当前控制器中实现代理协议中的方法

第一步:
设置控制器为UIScrollView的代理有2种方法:

方法一: 通过代码(self就是控制器)
self.scrollView.delegate = self;
方法二: 通过storyboard拖线(右击UIScrollView)

UIScrollView常用属性、代理方法

第二步,控制器应该遵守UIScrollViewDelegate协议UIScrollView常用属性、代理方法

第三步,实现协议中定义的相关方法

UIScrollView的3个代理方法

- (void)scrollViewDidScroll:
用户在滚动的时候
- (void)scrollViewWillBeginDragging:
用户即将开始拖拽的时候
- (void)scrollViewDidEndDragging:
用户拖拽完毕以后

代理设计模式的作用总结

1. 监听事件(返回来就是”通知”)

1> scrolView发生某某事件后,通知代理对象的某个方法
2> 代理对象的某个方法监听scrollView的某个事件(状态发生改变)

2. 回传数据
总结: 代理设计模式最终的目的就是”解耦”。

内容缩放

有些时候,我们可能要对某些内容进行手势缩放,如下图所示UIScrollView常用属性、代理方法

UIScrollView不仅能滚动显示大量内容,还能对其内容进行缩放处理
也就是说,要完成缩放功能的话,只需要将需要缩放的内容添加到UIScrollView中

通过 UIScrollView 实现内容缩放的思路

1. 拖拽一个UIScrollView
2. 向 UIScrollView 中添加内容(这里的内容就是指要进行缩放的内容(控件))
3. 通过代理监听缩放事件, 在缩放事件中返回 UIScrollView 的某个子控件(这个子控件就是告诉 UIScrollView 对这个控件进行缩放)
4. 设置缩放比:最大能放大多少倍, 最小能缩小百分之多少

UIScrollView的缩放原理

当用户在UIScrollView身上使用捏合手势时,UIScrollView会给代理发送一条消息,询问代理究竟要缩放自己内部的哪一个子控件(哪一块内容)

UIScrollView常用属性、代理方法当用户在UIScrollView身上使用捏合手势时,UIScrollView会调用代理的viewForZoomingInScrollView:方法,这个方法返回的控件就是需要进行缩放的控件
注意: UIScrollView 一次只能缩放一个子控件。

实现图片缩放的具体步骤

1. 添加UIScrollView, 设置UIScrollView的宽和高与控制器大小一致
2. 向UIScrollView中添加子控件UIImageView, 设置图片, 设置 UIImageView 的大小与图片的实际大小一致。
3. 设置UIScrollView的代理为当前控制器
4. 设置当前控制器遵守UIScrollViewDelegate代理协议
5. 在控制器中实现代理方法
– (UIView *)viewForZoomingInScrollView:方法返回要缩放的子控件
6. 在viewDidLoad中设置缩放比例

self.scrollView.maximumZoomScale = 2.0;
self.scrollView.minimumZoomScale = 0.4;

** 解释UIScrollViewDelegate中的@optional关键字、@required 关键字

其他与缩放相关的方法

即将开始缩放的时候调用
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
正在缩放的时候调用
(void)scrollViewDidZoom:(UIScrollView *)scrollView
缩放完毕的时候调用
- (void)scrollViewDidEndZooming::(UIScrollView *)scrollView

图片轮播器

实现思路:

1. 添加 UIScrollView
2. 动态向 UIScrollView 中添加图片框(横向)
3. 设置 UIScrollView 的 contentSize 实现滚动, 实现横向滚动
4. 实现分页
5. 实现分页指示器 UIPageControl
6. 通过使用 Nstimer 实现自动滚动

图片轮播器实现具体步骤

1. 创建一个UIScrollView, 设置宽为300, 高为130 (与每张图片的大小一致)
2. 向UIScrollView中添加内容(要滚动的内容, 添加到UIScrollView的子控件集合中)
循环添加5个UIImageView, 设置图片, 设置 frame
3. 设置UIScrollView的contentSize的width为5个图片的总大小, 上下不滚动所以height为0

UIScrollView常用属性、代理方法
4. 去掉水平滚动条

self.scrollView.showsHorizontalScrollIndicator = NO;

5. 实现自动分页

self.scrollView.pagingEnabled = YES;

问题: 设置完pagingEnabled = YES以后,scrollView是怎么知道该如何分页的?
答: 按照UIScrollView自身的宽度来实现分页的.UIScrollView的宽度就是每页的大小。
6. 显示分页指示器

1> 通过UIPageControl来实现
2> 拽一个UIPageControl放到控制器的view中, 不要放到UIScrollView中, 否则就一起滚动了.
3> 设置UIPageControl的Tint Color(其他页颜色)和Current Page(当前页颜色)属性颜色

注意: 当把UIPageControl添加到控制器的view中的时候, 这个控件和UIScrollView根本没有任何联系, 所以没有分页指示功能

7. 实现分页指示器总页数、当前页。
总页数: numberOfPages属性

self.pageControl.numberOfPages = imageCount;

当前页: currentPage属性

self.pageControl.currentPage = 0;

注意:
在 viewDidLoad 中设置总页数
在- (void)scrollViewDidScroll:代理方法中设置当前页
设置当前页的思路:
通过当前的滚动的偏移值来计算出当前滚动到第几页了

计算currentPage

UIScrollView常用属性、代理方法8. 通过定时器(NSTimer)实现自动滚动
在 viewDidLoad 中启动定时器
启动定时器的两种方法:

1> 调用timerWithXxx创建的timer,把这个timer对象手动加到”消息循环”中才能启动
2> 调用scheduledTimerWithXxx创建的timer,自动启动(创建完毕后自动启动)。

创建定时器的方法参见备注。

9. 在定时器的方法中实现滚动, 代码参考备注。
思路1:

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

10. 解决Bug
Bug: 当拖拽UIScrollView的时候, 保持一段时间不松手的时候, 一旦松手UIScrollView会连续滚动多次。
解决思路:在即将拖拽的时候, 停止计时器, 拖拽完毕后再打开一个计时器。
**停止计时器:调用 NSTimer 对象的 invalidate 方法(当某个计时器被停止以后, 就无法再重用了, 下次必须再重新创建一个新的计时器)。

[self.timer invalidate];
-(void)scrollViewWillBeginDragging:
-(void)scrollViewDidEndDragging:

11. 解决Bug
Bug:当单击(拖拽)界面上的某个其他控件的时候, UIScrollView停止滚动的问题。
产生 Bug 的原因:

当前处理UI界面的只有一个线程, 当这个线程处理UI的拖动事件的时候就没有能力再去处理滚动操作了

注意:

处理UI界面的的只能是一个线程。所以, 处理UIScrollView的滚动和其他控件的拖拽, 只能用同一个线程。如果多个线程都可以操作 UI 那么就会造成混乱的问题

解决思路:

提高处理滚动的timer的优先级。

注意:

所有控件的默认优先级都是NSRunLoopCommonModes ,但是网络和计时器对象默认的优先级要比控件的优先级低是NSDefaultRunLoopMode , 所以这里要把计时器的优先级调整为与控件一样的优先级NSRunLoopCommonModes。

分页

只要将UIScrollView的pageEnabled属性设置为YES,UIScrollView会被分割成多个独立页面,里面的内容就能进行分页展示
一般会配合UIPageControl增强分页效果,UIPageControl常用属性如下
一共有多少页

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

NSTimer

NSTimer叫做“定时器”,它的作用如下
在指定的时间执行指定的任务
每隔一段时间执行指定的任务
调用下面的方法就会开启一个定时任务

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

每隔ti秒,调用一次aTarget的aSelector方法,yesOrNo决定了是否重复执行这个任务
通过invalidate方法可以停止定时器的工作,一旦定时器被停止了,就不能再次执行任务。只能再创建一个新的定时器才能执行新的任务

- (void)invalidate;

ScrollView细节摘录及补充

scrollView的常用属性

1. contentSize 滚动范围
必须大于scrollview的 size
如果不想在横向滚动 设置为0 CGSizeMake(0,100);

2. 滚动指示条 , 默认是YES
showsHorizontalScrollIndicator
showsVerticalScrollIndicator

3. 即使不设置contentSize 依然可以有弹簧效果 , 前提是 bounces = YES
alwaysBounceVertical
alwaysBounceHorizontal

4. 弹簧效果
bounces 默认YES

5. contentInset 内边距, 滚动之后, content 距离 scrollView边界的距离
UIEdgeInsets (top,bottom,left,right)

6. contentOffset 偏移量, 滚动到了某个点
CGPoint

7. scrollEnabled
如果设置为NO的时候 , 将不能滚动

无法滚动的原因

1. contentSize 小于 scrollview的size
2. userInteractionEnabled = NO
3. _scrollView.scrollEnabled = NO

手动修改offset

// 在修改contentOffset的时候, 可以添加动画

[_scrollView setContentOffset:offset animated:YES];

delegate

scrollview滚动的时候, 会自动调用相应代理方法

对于使用者来说, 想要知道scrollview 滚动到某一个点的时候, 只需要干嘛?

1. 遵守协议
2. 设置自己成为代理
3.实现对应的代理方法

pageControl属性

UIPageControl
numberOfPages : 共有多少页
currentPage : 当前在第几页, 只能介于 0 .. numberOfPages – 1
currentPageIndicatorTintColor : 当前指示器的颜色
pageIndicatorTintColor : 非当前指示器的颜色

自动播放

计时器 : NSTimer

/**
interval : 间隔多长时间,调用 selector 对应的方法(changePages)
target : selector 的方法属于的对象 (控制器)
userInfo : 自定义的一些参数
repeats : 重复 ,
*/
_timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(changePages)
userInfo:nil
repeats:YES];

[_timer fire]; // 表示立即执行, 不会取等待 interval 时间
[_timer invalidate]; // 让计时器无效化, 想要再次使用, 就必须重新创建

优先级问题:

系统会优先处理用户交互事件, 当处理交互事件的时候, timer 会被暂停, 通过下面的方法进行优先级的修改:

NSRunLoop *mainLoop = [NSRunLoop mainRunLoop];
[mainLoop addTimer:_timer forMode:NSRunLoopCommonModes];

delegate 使用 weak 修饰符

防止互相强引用导致, 无法释放掉对象

如果有两个对象互相引用的时候, 必须有一个是弱引用 weak

下面是代理导致的强引用示意图UIScrollView常用属性、代理方法


本博客内容既有转载自网络的内容,也有本作者原创内容,仅供学习与交流之用
如有侵权或者错误之处,请及时在下方留言!
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址