iOS开发之UIScrollView控件详解

时间:2022-09-14 13:16:30

一、uiscrollview控件是什么?

    (1)移动设备的屏幕⼤大⼩小是极其有限的,因此直接展⽰示在⽤用户眼前的内容也相当有限

    (2)当展⽰示的内容较多,超出⼀一个屏幕时,⽤用户可通过滚动⼿手势来查看屏幕以外的内容

    (3)普通的uiview不具备滚动功能,不能显⽰示过多的内容

    (4)uiscrollview是一个能够滚动的视图控件,可以⽤用来展⽰示⼤大量的内容,并且可以通过滚 动查看所有的内容

    (5)  举例:手机上的“设置”、其他⽰示例程序

二、uiscrollview的简单使用

(1)将需要展⽰的内容添加到uiscrollview中

(2)设置uiscrollview的contentsize属性,告诉uiscrollview所有内容的尺⼨寸,也就是告诉 它滚动的范围(能滚多远,滚到哪⾥里是尽头)

注: 本文中所说的"内容视图"在官方文档中称作"content view",表示uiscrollview中可以用来展示内容的部分

三、属性与方法

内容视图相关

?
1
2
3
4
5
6
7
8
9
10
11
// 内容视图的大小,默认为cgsizezero
@property(nonatomic) cgsize contentsize;
 
// 为内容视图周围增加可滚动区域,默认为uiedgeinsetszero
@property(nonatomic) uiedgeinsets contentinset;
 
// 内容视图的原点相对于scrollview的原点的偏移量(左上方向偏移为正数),默认为cgpointzero
@property(nonatomic) cgpoint contentoffset;
 
// 设置内容视图的原点相对于scrollview的原点的偏移量
- (void)setcontentoffset:(cgpoint)contentoffset animated:(bool)animated;

滑动相关

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 是否允许滑动,默认为yes
@property(nonatomic,getter=isscrollenabled) bool scrollenabled;
 
// 是否只允许同时滑动一个方向,默认为no,如果设置为yes,用户在水平/竖直方向开始进行滑动,便禁止同时在竖直/水平方向滑动(注: 当用户在对角线方向开始进行滑动,则本次滑动可以同时在任何方向滑动)
@property(nonatomic, getter=isdirectionallockenabled) bool directionallockenabled;
 
// 是否允许点击状态栏让距离状态栏最近的scrollview滑动到顶部,默认为yes(注: 在iphone中如果有多个将该属性设置为yes的scrollview,则该方法无效;在ipad中则将距离状态栏最近的scrollview滑动到顶部)
@property(nonatomic) bool scrollstotop;
 
// 是否按页数进行滑动,默认为no,如果设置为yes,则在滑动时只会停止在scrollview的bounds的倍数处
@property(nonatomic, getter=ispagingenabled) bool pagingenabled;
 
// 是否有触底反弹效果,默认为yes
@property(nonatomic) bool bounces;
 
// 是否总是有触底反弹效果(即使内容视图小于scrollview的大小),默认为no(注: 生效的前提条件为bounces = yes)
@property(nonatomic) bool alwaysbouncehorizontal;
@property(nonatomic) bool alwaysbouncevertical;
 
// 指定用户手指离开屏幕后滑动减速的比率,默认为uiscrollviewdecelerationratenormal(慢慢停止),其余可选项为uiscrollviewdecelerationratefast(快速停止)
@property(nonatomic) cgfloat decelerationrate;
 
// 将指定区域滑动到刚好可见处(即距离边缘最近处)
- (void)scrollrecttovisible:(cgrect)rect animated:(bool)animated;

指示器相关

?
1
2
3
4
5
6
7
8
9
10
11
12
// 指示器样式,默认为uiscrollviewindicatorstyledefault(黑内容白边框,适用于任何背景),其余可选项为uiscrollviewindicatorstyleblack(全黑)和uiscrollviewindicatorstylewhite(全白)
@property(nonatomic) uiscrollviewindicatorstyle indicatorstyle;
 
// 为指示器周围增加可滚动区域,默认为uiedgeinsetszero
@property(nonatomic) uiedgeinsets scrollindicatorinsets;
 
// 是否在滑动时指示器可见,默认为yes
@property(nonatomic) bool showshorizontalscrollindicator;
@property(nonatomic) bool showsverticalscrollindicator;
 
// 闪一下指示器(注: 建议在scrollview展示给用户时调用一下,以提醒用户该处可滑动)
- (void)flashscrollindicators;

事件相关

uiscrollview处理触摸事件原理

当用户在uiscrollview的一个子视图上按下时,uiscrollview并不知道用户是想要滑动内容视图还是点击对应子视图,所以在按下的一瞬间,事件uievent从uiapplication传递到uiscrollview后,其会先将该事件拦截而不会立即传递给对应的子视图,同时开始一个150ms的倒计时,并监听用户接下来的行为

     1、当倒计时结束前,如果用户的手指发生了移动,则直接滚动内容视图,不会将该事件传递给对应的子视图;

     2、当倒计时结束时,如果用户的手指位置没有改变,则调用自身的-touchesshouldbegin:withevent:incontentview:方法询问是否将事件传递给对应的子视图(如果返回no,则该事件不会传递给对应的子视图,如果返回yes,则该事件会传递给对应的子视图,默认为yes)

     3、当事件被传递给子视图后,如果手指位置又发生了移动,则调用自身的-touchesshouldcancelincontentview:方法询问是否取消已经传递给子视图的事件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 返回是否用户已经触碰了内容视图准备进行滑动(注: 该值被设置为yes的时候可能用户只是触碰了内容视图,但是并没有开始进行滑动)
@property(nonatomic,readonly,getter=istracking) bool tracking;
 
// 返回是否用户已经开始滑动内容视图(注: 该值被设置为yes之前可能需要先滑动一段时间或距离)
@property(nonatomic,readonly,getter=isdragging) bool dragging;
 
// 返回是否处于减速状态(即手指已经离开屏幕,但scrollview仍然处于滑动中)
@property(nonatomic,readonly,getter=isdecelerating) bool decelerating;
 
// 是否延迟事件传递,默认为yes,如果设置为no,scrollview会立即调用-touchesshouldbegin:withevent:incontentview:方法以进行下一步操作
@property(nonatomic) bool delayscontenttouches;
 
// 是否可以取消内容视图被触摸,默认为yes,如果设置为no,则一旦开始跟踪事件,即使手指进行移动也不会取消已经传递给子视图的事件
@property(nonatomic) bool cancancelcontenttouches;
 
// 在uiscrollview的子类中重写该方法,用于返回是否将事件传递给对应的子视图,默认返回yes,如果返回no,该事件不会传递给对应的子视图
- (bool)touchesshouldbegin:(nsset<uitouch *> *)touches withevent:(uievent *)event incontentview:(uiview *)view;
 
// 在uiscrollview的子类中重写该方法,用于返回是否取消已经传递给子视图的事件,默认当子视图是uicontrol时返回no,否则返回yes(注: 该方法被调用的前提是cancancelcontenttouches = yes)
- (bool)touchesshouldcancelincontentview:(uiview *)view;

缩放相关

当用户使用两个手指进行缩放操作时,我们调整内容视图的偏移量和缩放比例(注: 用户两个手指操作结束后,有可能仍然有一个手指在操作,这时不会将事件传递给子视图)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 最小缩放比例,默认为1.0
@property(nonatomic) cgfloat minimumzoomscale;
 
// 最大缩放比例,默认为1.0(必须大于minimumzoomscale才能正常工作)
@property(nonatomic) cgfloat maximumzoomscale;
 
// 缩放比例,默认为1.0
@property(nonatomic) cgfloat zoomscale;
 
// 设置缩放比例
- (void)setzoomscale:(cgfloat)scale animated:(bool)animated;
 
// 缩放到指定区域
- (void)zoomtorect:(cgrect)rect animated:(bool)animated;
 
// 是否允许触底反弹,默认为yes
@property(nonatomic) bool bounceszoom;
 
// 返回是否正在缩放
@property(nonatomic,readonly,getter=iszooming) bool zooming;
 
// 返回是否正在触底反弹
@property(nonatomic,readonly,getter=iszoombouncing) bool zoombouncing;

键盘相关

?
1
2
// 隐藏键盘模式,默认为uiscrollviewkeyboarddismissmodenone(不隐藏键盘),其余可选项为uiscrollviewkeyboarddismissmodeondrag(当拖拽scrollview时隐藏键盘)和uiscrollviewkeyboarddismissmodeinteractive(当拖拽键盘上方时隐藏键盘)
@property(nonatomic) uiscrollviewkeyboarddismissmode keyboarddismissmode;

代理

滑动相关

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 当scrollview的contentoffset发生变化时调用
- (void)scrollviewdidscroll:(uiscrollview *)scrollview;
 
// 将要开始拖拽时调用(注: 该方法可能需要先滑动一段时间或距离才会被调用)
- (void)scrollviewwillbegindragging:(uiscrollview *)scrollview;
 
// 当用户停止拖拽时调用(注: 应用程序可以通过修改targetcontentoffset参数的值来调整内容视图content view停止的位置)
- (void)scrollviewwillenddragging:(uiscrollview *)scrollview withvelocity:(cgpoint)velocity targetcontentoffset:(inout cgpoint *)targetcontentoffset;
 
// 当用户停止拖拽时调用(注: 如果内容视图content view在停止拖拽后继续移动,则decelerate参数为yes)
- (void)scrollviewdidenddragging:(uiscrollview *)scrollview willdecelerate:(bool)decelerate;
 
// 将要开始减速时调用(仅当停止拖拽后继续移动时才会被调用)
- (void)scrollviewwillbegindecelerating:(uiscrollview *)scrollview;
 
// 已经结束减速时调用(仅当停止拖拽后继续移动时才会被调用)
- (void)scrollviewdidenddecelerating:(uiscrollview *)scrollview;
 
// 返回是否允许点击状态栏让scrollview滑动到顶部,如果未实现该方法,则默认为yes(仅当scrollstotop属性为yes时才调用)
- (bool)scrollviewshouldscrolltotop:(uiscrollview *)scrollview;
 
// 当scrollview已经滑动到顶部时调用(仅当点击状态栏让scrollview滑动到顶部才调用)
- (void)scrollviewdidscrolltotop:(uiscrollview *)scrollview;
 
// 当-setcontentoffset:animated:/-scrollrectvisible:animated:方法动画结束时调用(仅当animated设置为yes时才调用)
- (void)scrollviewdidendscrollinganimation:(uiscrollview *)scrollview;

缩放相关

?
1
2
3
4
5
6
7
8
9
10
11
// 当缩放比例更改时调用
- (void)scrollviewdidzoom:(uiscrollview *)scrollview;
 
// 参与缩放的子视图
- (uiview *)viewforzoominginscrollview:(uiscrollview *)scrollview;
 
// 将要开始缩放时调用
- (void)scrollviewwillbeginzooming:(uiscrollview *)scrollview withview:(uiview *)view;
 
// 已经结束缩放时调用
- (void)scrollviewdidendzooming:(uiscrollview *)scrollview withview:(uiview *)view atscale:(cgfloat)scale;

常见需求原理解析

导航栏半透明效果

iOS开发之UIScrollView控件详解

原理解析:

默认情况下,在有uinavigationbar存在时,系统为了防止uiscrollview被遮挡,其contentinset和scrollindicatorinsets属性都会被设置为uiedgeinsetsmake(64, 0, 0, 0);在有uitabbar存在时,系统为了防止uiscrollview被遮挡,其contentinset和scrollindicatorinsets属性都会被设置为uiedgeinsetsmake(0, 0, 49, 0)

因此,为了使用此种半透明效果,可以直接将uiscrollview的frame设置为整个屏幕的大小

    注1: 系统只在uiscrollview是控制器视图的第0个子视图时才会自动修改contentinset和scrollindicatorinsets属性

    注2: 如果不想让系统自动修改contentinset和scrollindicatorinsets属性,可以设置self.automaticallyadjustsscrollviewinsets = no;

控件悬停

iOS开发之UIScrollView控件详解

原理解析:

    方式一: 在悬停位置放置一个与待悬停控件相同的控件,通过-scrollviewdidscroll:代理方法跟踪contentoffset的的变化,当不满足悬停条件时,将该控件hidden属性设置为yes;当满足悬停条件时,将该控件hidden属性设置为no

    方式二: 通过-scrollviewdidscroll:代理方法跟踪contentoffset的的变化,当不满足悬停条件时,待悬停控件属于uiscrollview的子视图,当满足悬停条件时,待悬停控件属于uiscrollview的父视图的子视图

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 以"方式二"为例
- (void)scrollviewdidscroll:(uiscrollview *)scrollview
{
 if (scrollview.contentoffset.y >= 100)
 {
  cgrect rect = label.frame;
  rect.origin.y = 0;
  label.frame = rect;
 
  [self.view addsubview:label];
 }
 else
 {
  cgrect rect = label.frame;
  rect.origin.y = 100;
  label.frame = rect;
 
  [scrollview addsubview:label];
 }
}

下拉头部图片放大

iOS开发之UIScrollView控件详解

原理解析:

通过-scrollviewdidscroll:代理方法跟踪contentoffset的的变化,根据contentoffset动态设置图片的缩放比例

?
1
2
3
4
5
6
7
8
// 以"动态修改图片缩放比例于1倍和2倍之间"为例
- (void)scrollviewdidscroll:(uiscrollview *)scrollview
{
 cgfloat scale = 1 - (scrollview.contentoffset.y / 100);
 scale = (scale >= 1) ? scale : 1;
 scale = (scale <= 2) ? scale : 2;
 imageview.transform = cgaffinetransformmakescale(scale, scale);
}

图片无限轮播

iOS开发之UIScrollView控件详解

原理解析:

在已知图片数组有n个元素前提下,在uiscrollview中创建n+2个uiimageview,其中第1个至第n个图片为真实内容,第0个与第n个一样,第n+1个与第1个一样,通过-scrollviewdidscroll:代理方法跟踪contentoffset的的变化,在滑动到首尾两个图片处直接设置contentoffset到真实图片处即可

iOS开发之UIScrollView控件详解

总结

以上就是这篇文章的全部内容了,希望本文的内容对各位ios开发者们能有所帮助,如果有疑问大家可以留言交流。