翻译-苹果官方文档-《iOS滚动视图编程指南》(Scroll View Programming Guide for iOS)

翻译-苹果官方文档-《iOS滚动视图编程指南》(Scroll View Programming Guide for iOS)

翻译到Basic Zooming Using Gestures,未完待续,另外翻译的很烂,虽然可能根本没人看😢

关于滚动视图编程

在iOS应用中,当需要备展示或操作的内容无法适应设备屏幕大小时,就要使用scrll views(滚动视图)。滚动视图主要有两个作用:

  • 允许用户拖拽想要显示的内容区域

  • 允许用户利用拖拽手势放大或缩小所显示的内容

下图显示了一个典型的UIScrollView类的应用场景。UIImageView类的子类包含了一个小男孩的照片。当用户在屏幕上拖拽手指时,照片上的视图窗口不断移动,正如读者看到的,滚动指示器在窗口中出现。当用户抬起手指,指示器消失。

概览

UIScrollView类可以提供以下功能:

  • 滚动不能完全适应屏幕的内容

  • 缩放内容,允许应用支持标准的缩放手势

  • 将内容滚动限制为每次滚动都显示一整屏的内容(翻页模式

UIScrollView类并不包含为其所显示内容所定义的特殊的视图;相反,它会对自己的子视图进行滚动。由于在iOS上发起滚屏不需要任何额外的控件,所以这种简单的模型是可行的。

基础滚屏非常容易实现

通过drag(拖拽)和flick(拨动)进行滚屏不需要实现子类或指定代理。只要能够恰当的设置UIScrollView类实例对象所显示的内容的尺寸,整个交互界面可以通过界面编辑器设计和创建。

相关章节:创建和配置滚动视图

要支持拖拽缩放手势,需要使用代理

添加基本的拖拽缩小和拖拽放大支持需要让滚屏视图使用代理。代理类必需遵守UIScrollViewDelegate协议,而且必需实现能够表明滚屏视图的那个子视图应该被缩放的代理方法。读者必需设定最小和最大缩放系数中的一个或两个都设置。

如果应用需要支持双击放大,两指触摸缩小,以及简单的单触滚屏和移动(除了标准的拖拽缩放意外的熟悉),就需要在显示内容的视图中实现代码,满足这些功能。

相关章节:使用拖拽手势的基本缩放

要支持拖拽缩放和点击缩放,需在内容视图中实现代码

如果应用需要支持双击放大,两指触摸缩小,以及简单的单触滚屏和移动(除了标准的拖拽缩放意外的熟悉),就需要在显示内容的视图中实现代码。

相关章节:点击缩放

要支持翻页模式,只需要三个子视图

要支持翻页模式,不需要创建子类或使用代理。只需设定内容的尺寸并开启翻页模式即可。只需三个子视图就可以实现翻页模式的大部分功能,这样即节省了内容空间,也提升了性能。

相关注释:使用翻页模式滚屏

阅读前提

阅读本篇指南之前,请先阅读App Programming Guide for iOS来了解开发iOS应用的基本流程。同样考虑阅读View Controller Programming Guide for iOS以了解视图控制器的基本信息,因为其经常会同滚屏视图配合使用。

如何使用本指南

本指南接下来的章节会带读者逐步深入各种复杂的任务,如处理点击放大技术,了解代理的角色,代理的消息序列,以及在应用中嵌套滚屏视图。

相关拓展

下面列出的示例代码对于读者实现自己的滚屏视图十分具有指导意义:

  • scrolling展示le基本滚屏

  • PageControl展示了在页面模式下使用滚屏视图

  • 示例项目ScrollViewSuite。这些是高端的示例代码,展示了诸如点击滚动和其它高级技术,包括使用块视图来展示大型图片,同时有效的使用内存。

创建和配置滚动视图

同其他视图一样,滚动视图也可以通过代码或界面编辑器创建 。只需要一些额外的设置,就可以获得基本的滚动能力。

创建和配置滚动视图

同其他视图一样,滚动视图被创建之后也会被加入一个视图控制器或视图结构中。只需要额外的两部就可以完成滚动视图的配置:

  1. 必需通过conetentSize属性来设置可滚动内容的大小。这个属性表明了可滚动区域的大小。

  2. 必需添加一个或多个视图,来被滚动视图显示或滚动。这些视图提供了显示内容。

另外如果需要的话还可以为应用设置视觉提示——垂直和水平的滚动指示器,拖拽回弹,缩放回弹,以及滚动方向限制。

在界面编辑器中创建滚动视图

要在界面编辑器中创建滚动视图,可以将UIScrollView的图标从Library->Cocoa Touch->Data Views选项板中拖拽到视窗中。然后将UIViewController子类的视图出口同滚屏视图链接起来。图1-1显示了这个链接,假设File's Owner就是UIViewController的子类(这是一种常见的设计模式)。

即使在界面编辑器中的UIScrollView检视面板允许读者设置滚动视图的许多属性,但是读者仍然需要在应用代码中设置contentSize属性,其定义了可滚动范围的尺寸。如果将一个滚动视图设置为一个视图控制器的view属性(一般情况下这个视图控制器会是File's Owner),就需要在视图控制器的viewDidLoad方法中初始化contentSize属性,如表1-1。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIScrollView *tempScrollView=(UIScrollView *)self.view;
    tempScrollView.contentSize=CGSizeMake(1280,960);
}

设置好滚动视图的尺寸后,应用可以向其添加子视图,提供需显示的内容,可以通过的编程的方式也可以利用界面编辑器。

从代码创建滚动视图

同样也可以完全用代码创建滚动视图。这项工作一般在视图控制器类的代码中完成,特别是在loadView方法中。示例实现代码如表1-2所示。

- (void)loadView {
    CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
    scrollView=[[UIScrollView alloc] initWithFrame:fullScreenRect];
    scrollView.contentSize=CGSizeMake(320,758);
    // do any further configuration to the scroll view
    // add a view, or views, as a subview of the scroll view.
    // release scrollView as self.view retains it
    self.view=scrollView;
    [scrollView release];
}

上面的代码创建了一个大小为全屏的滚动视图(减去状态栏),将滚动视图对象设置为视图控制器的view,并将contentSize属性设置为320 * 758像素(有错,应该为点)。这个代码创建了一个可以垂直滚动的滚动视图。

在真正的实现中会包含更多代码。例如,需要在代码中加入子视图或视图,并按要求配置它们。同样,代码假设视图控制器还没有视图,如有,需要释放已有的视图并将滚动视图设置为控制器的视图。

添加子视图

创建和配置滚动视图之后,必需添加一个或多个子视图来展示内容。使用多少子视图完全取决于一个要求:滚动视图是否需要支持缩放?

如果想要支持缩放,最常见的技术是使用单一视图,包含整个滚动视图的contentSize(可滚动范围),然后为那个子视图添加额外的子视图。这样就可以将包含其他视图的视图作为缩放视图,而其子视图将会根据起状态自行缩放。

如果不要求缩放,那么不管使用单一子视图(不管有没有子视图)还是多个子视图都是可行的。

注:尽管使用单一子视图是常见的方式,但读者的应用也可能要求在同一个滚动视图中使用多个子视图来支持缩放。那样,就需要使用代码方法viewForZoomingInScrollView来返回合适的子视图,详见章节使用拖拽手势实现基本缩放

设置滚动视图的内容尺寸,内容衬垫,以及滚动指示器

contentSize属性是需要在滚动视图中显示的内容的尺寸。在界面编辑器中创建滚动视图时,它被设置为320宽,758高。图1-2的图片表示了contentSize所表示的滚动视图内容的宽和高。

可能需要在滚动视图的内容周围加上一些衬垫空间,一般在整个内容的上部和下部,这样控制器和工具栏不会对内容产生干扰。为了加入衬垫,使用contentInset属性了为内容设置一个缓冲区。这样就可以在怪变子视图尺寸或视图内容尺寸的情况下使得整个内容区域变得更大。

contentInset属性是一个UIEdgeInsets结构体,包含内容的上,左,下,右边界分别到视图边缘的距离。图1-3解释了contentInsetcontentSize分别表示什么。

如图1-3所示,contentInset属性(64, 0 , 44, 0)会导致上部的衬垫空间为64(20留给状态栏,44留给导航控制器),下不为44(工具栏的高度)。这样设置contentInset属性可以让导航共建和状态空间显示在屏幕上,并且还可以完整的显示滚动视图的整个内容。

- (void)loadView {
    CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
    scrollView=[[UIScrollView alloc] initWithFrame:fullScreenRect];
    self.view=scrollView;
    scrollView.contentSize=CGSizeMake(320,758);
    scrollView.contentInset=UIEdgeInsetsMake(64.0,0.0,44.0,0.0);
    // do any further configuration to the scroll view
    // add a view, or views, as a subview of the scroll view.
    // release scrollView as self.view retains it
    self.view=scrollView;
    [scrollView release];
}

图1-4显示了设置contentSize之后的效果。当窗口滚动至最上方时(如左图),屏幕为导航栏和状态栏预留了空间。右边的图片显示了当窗口滚动至最下方时,为工具栏预留了空间。在这两种情况中可以看到当内容从导航栏或工具栏下方滚动时,它们都是透明的,但是当滚动至最上方或最下方时,所有的内容都是无遮挡的。

然而,改变contentInset的值会导致一个意想不到的副作用,当读者滚动视图显示滚动指示器时。当用户拖拽内容至最上方或最下方时,滚动指示器会出现在由contentInset属性所定义的范围内的内容上,例如,它会出现在导航栏或工具栏上。

为了修正这个错误,必需设置scrollIndicatorInsets属性。同contentInset属性一样,scrollIndicatorInsets属性也是UIEdgeInsets结构体。设置垂直方向的衬垫值会限制垂直方向的滚动指示器显示在衬垫意外的范围,而且还会导致水平指示器显示在contentInset所定义的矩形以外的范围。

设置contentInset属性而不调整scrollIndicatorInsets属性会导致滚动指示器被绘制在导航控件和工具栏上方,这不是我们想要的结果。所以,正确设置scrollIndicatorInsets属性来同contentInset属性配合会修正这一问题。

表1-4显示了修正过后的loadView方法,添加了scrollIndicatorInsets属性的初始值。

- (void)loadView {
    CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
    scrollView=[[UIScrollView alloc] initWithFrame:fullScreenRect];
    scrollView.contentSize=CGSizeMake(320,758);
    scrollView.contentInset=UIEdgeInsetsMake(64.0,0.0,44.0,0.0);
    scrollView.scrollIndicatorInsets=UIEdgeInsetsMake(64.0,0.0,44.0,0.0);
    // do any further configuration to the scroll view
    // add a view, or views, as a subview of the scroll view.
    // release scrollView as self.view retains it
    self.view=scrollView;
    [scrollView release];
}

滚动滚动视图的内容

最常见的发起滚动视图开始滚动的方式是由用户触摸屏幕并用手指拖拽。内容的滚动是对动作的回应。这个手势被称为拖动手势(drag)

拖拽手势延伸出滑动手势。滑动是用户使用手指在屏幕上进行快速的接触,并将手指滑向想要滚动的方向,然后手指离开屏幕。这个手势不仅会造成滚动,还会给滚动施加一个动量,基于用户滑动动作的速度,会让视图在手持完成后继续滚动。滚动在一段事件里会慢慢减速至停止。滑动手势允许用户使用一个动作就让视图运行一大段距离。在减速时,用户可以通过触摸屏幕来停止滚动。所有这些行为都内建在UIScrollView类当中,不需要开发者来实现。

但有些时候需要应用通过代码的方式进行滚动内容,例如,为了展示一个文件的特定部分。在这种情况下,UIScrollView会提供必须方法来实现这一效果。

UIScrollView类的代理协议UIScrollViewDelegate提供了允许应用追中滚动进度,以及响应应用的特定要求的方法。

利用代码实现滚动

滚动滚动视图的内容并不总是为了响应用户在屏幕上的拖动或滑动手势。有时应用需要将视图滚动至特定的内容,以显示一个特定的矩形区域,或者要滚动至视图的最上部。UIScrollView类提供了实现这些动作的方法。

滚动至偏移位置

滚动至一个特定的左上位置(contentOffset属性)可以通过两种方式完成。setContentOffset:animated:方法会将内容滚动至特定的偏移位置。如果动画参数为YES,从现有位置到特定位置的滚动会被加上一个特定速率的动画效果。如果动画参数是NO,滚动会立即完成,不会有动画效果。在上面的两种情况下,都会向代理发送scrollViewDidScroll:消息。如果关闭动画,或通过contentOffset属性直接设置偏移量,代理只会收到scrollViewDidScroll:消息。如果开启动画,代理会在动画进行过程中收到一系列scrollViewDidScroll:消息。当动画完成时,代理收到scrollViewDidEndScrollingAnimation:消息。

显示矩形

还可以滚动一个矩形区域,让其可见。这在当应用需要显示当前处在可见区域外的控件时十分有用。scrollRectToVisible:animated:方法会滚动特定矩形,使其在滚动视图中可见。如果动画参数为YES,矩形会在特定速率下被滚动至视图。同setContentOffset:animated一样,如果关闭动画效果,代理只会收到一条scrollViewDidScroll:消息。如果动画开启,代理会在动画过程中收到一系列scrollViewDidScroll:消息。在调用scrollRectToVisible:animated:方法期间,滚动视图的追踪和属性都为NO。

scrollRectToVisible:animated:中开启了动画,代理最后会收到scrollViewDidEndScrollingAnimation:消息,作为滚动视图已经到达特定地点,动画完结的通知。

滚动至上部

在状态栏可见的情况下,可以通过单击状态栏的方式使滚动视图滚动至最上方。这个动作在展示一组垂直排列的数据的应用里非常常见。例如,照片应用支持滚动到顶部,在相册的选择视图中和相片列表视图中都可以,大多数UIView类的实现(UIScrollView的子类)也支持滚动至顶部。

应用可通过实现滚动视图代理协议中的scrollViewShouldScrollToTop:并返回YES来实现这个功能。这个代理方法允许允许屏幕上存在多个滚动视图时,返回其中某一个,将其的窗口滚动至最上部。

当滚动完成时,代理会收到scrollViewDidScrollToTop:消息,表明完成动作的视图对象。

在滚动期间发送代理消息

在滚动期间,滚动视图会使用trackingdraggingdecelerating以及zooming属性来追踪滚动视图的状态。除此之外,contentOffset属性定义了内容中同滚动视图的左上边界的一个可见点。下面的表格描述了这些属性:

  • tracking:如果用户的手指在接触设备屏幕,属性值为YES

  • dragging:如果用户的手指接触屏幕并拖动,属性值为YES

  • decelerating:如果视图由于滑动手势而产生减速运动,或由于拖动手势导致视图移出滚动视图的框架范围而产生回弹效果,属性值为YES

  • zooming:如果滚动视图正在追踪一个改变其zoomScale属性的捏合动作,属性值为YES

  • contentOffset:一个CGPoint值,定义了滚动视图边界左上角的一个点

不需要通过监控这些属性来决定滚动过程中的动作,因为滚动视图会向其代理发送一个详细的信息序列,表示滚动动作的进程。这些方法允许应用根据需要响应。代理方法可以查询状态属性来决定是否要接收这些消息或者滚动视图当前的位置。

简单方法:追踪一个滚动动作的起始和结束

如果应用只对滚动过程的起始和结束感兴趣,那么只要实现代理方法的一小部分即可。

实现scrollViewBeginDragging:方法,以便当拖动开始时接收通知。

为了确定滚动是否完成,必需实现两个代理方法:scrollViewDidEndDragging:willDecelerate:scrollViewDidEndDecelerating:方法。滚动的结束要么是代理对象收到scrollViewDidEndDragging:willDecelerate:消息,其中减速参数为NO,要么是代理收到scrollViewDidEndDecelerating:消息。不管那种情况,滚动都已经完成。

完整的代理消息序列

当用户触摸屏幕时追踪序列开始。tracking属性立刻被设置为YES,并且只要用户的手指在接触屏幕,都会为YES,不管手指是否移动。

如果手指保持精致,而且内容视图响应了触摸事件,那么就应该处理触摸事件,序列结束。

然而,如果用户移动手指,序列会继续。

当用户开始移动手指,发起滚动视图,视图会第一次尝试(假设这是滚动视图的默认值)取消所有进行中的事件处理,如果它正在尝试这样做。

注:贯穿整个消息序列,很有可能trackingdragging属性会一直保持为NO,zooming属性保持为YES。这种情况发生在滚动的结果是缩放动作时,不管滚动是由手势还是代码引起的。当然,如果需要,读者可以让由缩放或滚动产生的代理方法选择其它的动作。

滚动视图的dragging属性设置为YES,其代理会收到scrollViewWillBeginDragging:消息。

随着用户拖动手指,scrollViewDidScroll:消息会发送给代理。这个消息会随着滚动的进行而不断发送。这个方法的实现可以查询滚动视图的contentOffset属性来确定滚动视图边界左上角的位置。contentOffset属性总是滚动视图边界左上角的当前位置,不管视图是否在滚动。

如果读者使用滑动手势,tracking属性会被设置为NO,这是为了实现滑动手势,用户的手指在接触到屏幕的一瞬间,会使得视图开始滑动,随后会离开屏幕。此时,代理会收到scrollViewDidEndDragging:willDecelerate:消息。由于滚动需要减速,所以减速参数为YES。减速的速度由decelerationRate属性控制。默认情况下,这个属性的值是UIScrollViewDecelerationRateNormal,会使得滚动持续相当长的一段时间。也可将速率设置为UIScrollViewDecelerationFast来让减速时间大大减少,那么滑动手指之后的滚动距离会大大减少。在视图减速期间,decelerating属性会被设置为YES。

如果用户拖动,停止拖动以及将手指从屏幕移开,代理会收到scrollViewDidEnding:willDecelerate:消息,然而减速参数为NO。这是因为用户并没有给滚动视图施加动量。由于用户的手指不再接触屏幕,此时tracking属性会被设置为NO。

如果scrollViewDidEndDragging:willDecelerate消息的减速参数是NO,那么滚动视图的代理对象就不会再收到关于这个拖动动作的代理消息。滚动视图的减速属性因此也会返回NO。

还有一种情况会导致scrollViewDidEndDragging:willDecelerate发送给代理,即使用户在手指静止的状态下将手指抬起。如果滚动视图被设置为在用户将内容拖动至滚动边界以外时会产生回弹的视觉效果时,scrollViewDidEndDragging:willDecelerate消息会发送给代理对象,而且减速参数会为YES。当bounces属性设为YES会开启回弹(默认值)。当bounces属性为NO时,alwaysNouceVerticalalwaysBounceHorizontal属性不会影响滚动视图的行为。如果bounces为YES,它们会在contentSize属性小于视图边界的情况下允许回弹。(bounces属性开启时,内容尺寸大于视图尺寸时会回弹)

不管在何种情况下视图会发送scrollViewDidEndDragging:willDecelerate消息,如果减速参数为YES,滚动视图就会发送scrollViewWillBeginDecelerating:消息。在减速期间,代理继续接收scrollViewdidScroll:消息,尽管当前trackingdragging属性的值都是NO。decelerating属性继续为YES。

最后,当滚动视图的减速结束时,代理会收到scrollViewDidEndDecelerating:消息,decelerating属性为NO,滚动序列结束。

捏合手势的基本缩放

通过点击缩放

使用翻页模式滚动

嵌套滚动视图

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容