您的位置:首页 > 产品设计 > UI/UE

iphone开发之触摸事件详解

2012-04-19 20:30 218 查看
现在的应用中大都支持触摸操作,如果应用中加入触摸事件会提高用户体验性。今天我们就来学习ios下触摸事件,先来看官方说明文档:


触摸事件

iPhoneOS中的触摸事件基于多点触摸模型。用户不是通过鼠标和键盘,而是通过触摸设备的屏幕来操作对象、输入数据、以及指示自己的意图。iPhoneOS将一个或多个和屏幕接触的手指识别为多点触摸序列的一部分,该序列从第一个手指碰到屏幕开始,直到最后一个手指离开屏幕结束。iPhoneOS通过一个多点触摸序列来跟踪与屏幕接触的手指,记录每个手指的触摸特征,包括手指在屏幕上的位置和发生触摸的时间。应用程序通常将特定组合的触摸识别为手势,并以用户直觉的方式来进行响应,比如对收缩双指距离的手势,程序的响应是缩小显示的内容;对轻拂屏幕的手势,则响应为滚动显示内容。

请注意:手指在屏幕上能达到的精度和鼠标指针有很大的不同。当用户触击屏幕时,接触区域实际上是椭圆形的,而且比用户想像的位置更靠下一点。根据触摸屏幕的手指、手指的尺寸、手指接触屏幕的力量、手指的方向、以及其它因素的不同,其“接触部位”的尺寸和形状也有所不同。底层的多点触摸系统会分析所有的这些信息,为您计算出单一的触点。

很多UIKit类对多点触摸事件的处理方式不同于它的对象实例,特别是像
UIButton
UISlider
这样的
UIControl
的子类。这些子类的对象—被称为控件对象—只接收特定类型的手势,比如触击或向特定方向拖拽。控件对象在正确配置之后,会在某种手势发生后将动作消息发送给目标对象。其它的UIKit类则在其它的上下文中处理手势,比如
UIScrollView
可以为表格视图和具有很大内容区域的文本视图提供滚动行为。

某些应用程序可能不需要直接处理事件,它们可以依赖UIKit类实现的行为。但是,如果您创建了
UIView
定制子类—这是iPhone
OS系统开发的常见模式—且希望该视图响应特定的触摸事件,就需要实现处理该事件所需要的代码。而且,如果您希望一个UIKit对象以不同的方式响应事件,就必须创建框架类的子类,并重载相应的事件处理方法。


事件和触摸

在iPhoneOS中,触摸动作是指手指碰到屏幕或在屏幕上移动,它是一个多点触摸序列的一部分。比如,一个pinch-close手势就包含两个触摸动作:即屏幕上的两个手指从相反方向靠近对方。一些单指手势则比较简单,比如触击、双击、或轻拂(即用户快速碰擦屏幕)。应用程序也可以识别更为复杂的手势,举例来说,如果一个应用程序使用具有转盘形状的定制控件,用户就需要用多个手指来“转动”转盘,以便进行某种精调。

事件是当用户手指触击屏幕及在屏幕上移动时,系统不断发送给应用程序的对象。事件对象为一个多点触摸序列中所有触摸动作提供一个快照,其中最重要的是特定视图中新发生或有变化的触摸动作。一个多点触摸序列从第一个手指碰到屏幕开始,其它手指随后也可能触碰屏幕,所有手指都可能在屏幕上移动。当最后一个手指离开屏幕时,序列就结束了。在触摸的每个阶段,应用程序都会收到事件对象。

触摸信息有时间和空间两方面,时间方面的信息称为阶段(phrase),表示触摸是否刚刚开始、是否正在移动或处于静止状态,以及何时结束—也就是手指何时从屏幕举起(参见图3-1)。触摸信息还包括当前在视图或窗口中的位置信息,以及之前的位置信息(如果有的话)。当一个手指接触屏幕时,触摸就和某个窗口或视图关联在一起,这个关联在事件的整个生命周期都会得到维护。如果有多个触摸同时发生,则只有和同一个视图相关联的触摸会被一起处理。类似地,如果两个触摸事件发生的间隔时间很短,也只有当它们和同一个视图相关联时,才会被处理为多触击事件。

图3-1 多点触摸序列和触摸阶段



在iPhoneOS中,一个
UITouch
对象表示一个触摸,一个
UIEvent
对象表示一个事件。事件对象中包含与当前多点触摸序列相对应的所有触摸对象,还可以提供与特定视图或窗口相关联的触摸对象(参见图3-2)。在一个触摸序列发生的过程中,对应于特定手指的触摸对象是持久的,在跟踪手指运动的过程中,UIKit会对其进行修改。发生改变的触摸属性变量有触摸阶段、触摸在视图中的位置、发生变化之前的位置、以及时间戳。事件处理代码通过检查这些属性的值来确定如何响应事件。

图3-2 
UIEvent
对象及其
UITouch
对象间的关系



系统可能随时取消多点触摸序列,进行事件处理的应用程序必须做好正确响应的准备。事件的取消可能是由于重载系统事件引起的,电话呼入就是这样的例子。


事件的传递

系统将事件按照特定的路径传递给可以对其进行处理的对象。如“核心应用程序架构”部分描述的那样,当用户触摸设备屏幕时,iPhone
OS会将它识别为一组触摸对象,并将它们封装在一个
UIEvent
对象中,放入当前应用程序的事件队列中。事件对象将特定时刻的多点触摸序列封装为一些触摸对象。负责管理应用程序的
UIApplication
单件对象将事件从队列的顶部取出,然后派发给其它对象进行处理。典型情况下,它会将事件发送给应用程序的键盘焦点窗口—即拥有当前用户事件焦点的窗口,然后代表该窗口的
UIWindow
对象再将它发送给第一响应者进行处理(第一响应者在 “响应者对象和响应者链”部分中描述)。

应用程序通过触碰测试(hit-testing)来寻找事件的第一响应者,即通过递归调用视图层次中视图对象的
hitTest:withEvent:
方法来确认发生触摸的子视图。触摸对象的整个生命周期都和该视图互相关联,即使触摸动作最终移动到该视图区域之外也是如此。“事件处理技巧”部分对触碰测试在编程方面的一些隐含意义进行讨论。

UIApplication
对象和每个
UIWindow
对象都在
sendEvent:
方法(两个类都声明了这个方法)中派发事件。由于这些方法是事件进入应用程序的通道,所以,您可以从
UIApplication
UIWindow
派生出子类,重载
其sendEvent:
方法,实现对事件的监控或执行特殊的事件处理。但是,大多数应用程序都不需要这样做。


响应者对象和响应者链

响应者对象是可以响应事件并对其进行处理的对象。
UIResponder
是所有响应者对象的基类,它不仅为事件处理,而且也为常见的响应者行为定义编程接口。
UIApplication
UIView
、和所有从
UIView
派生出来的UIKit类(包括
UIWindow
)都直接或间接地继承自
UIResponder
类。

第一响应者是应用程序中当前负责接收触摸事件的响应者对象(通常是一个
UIView
对象)。
UIWindow
对象以消息的形式将事件发送给第一响应者,使其有机会首先处理事件。如果第一响应者没有进行处理,系统就将事件(通过消息)传递给响应者链中的下一个响应者,看看它是否可以进行处理。

响应者链是一系列链接在一起的响应者对象,它允许响应者对象将处理事件的责任传递给其它更高级别的对象。随着应用程序寻找能够处理事件的对象,事件就在响应者链中向上传递。响应者链由一系列“下一个响应者”组成,其顺序如下:

第一响应者将事件传递给它的视图控制器(如果有的话),然后是它的父视图。

类似地,视图层次中的每个后续视图都首先传递给它的视图控制器(如果有的话),然后是它的父视图。

最上层的容器视图将事件传递给
UIWindow
对象。

UIWindow
对象将事件传递给
UIApplication
单件对象。

如果应用程序找不到能够处理事件的响应者对象,则丢弃该事件。

响应者链中的所有响应者对象都可以实现
UIResponder
的某个事件处理方法,因此也都可以接收事件消息。但是,它们可能不愿处理或只是部分处理某些事件。如果是那样的话,它们可以将事件消息转送给下一个响应者,方法大致如下:

-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event{

UITouch*touch=[touchesanyObject];

NSUIntegernumTaps=[touchtapCount];

if(numTaps<2){

[self.nextRespondertouchesBegan:toucheswithEvent:event];

}else{

[selfhandleDoubleTap:touch];

}

}

请注意:如果一个响应者对象将一个多点触摸序列的初始阶段的事件处理消息转发给下一个响应者(在
touchesBegan:withEvent:
方法中),
就应该同样转发该序列的其它事件处理消息。

动作消息的处理也使用响应者链。当用户对诸如按键或分页控件这样的
UIControl
对象进行操作时,控件对象(如果正确配置的话)会向目标对象发送动作消息。但是,如果目标对象被指定为
nil
,应用程序就会像处理事件消息那样,把该动作消息路由给第一响应者。如果第一响应者没有进行处理,再发送给其下一个响应者,以此类推,将消息沿着响应者链向上传递。


调整事件的传递

UIKit为应用程序提供了一些简化事件处理、甚至完全关闭事件流的编程接口。下面对这些方法进行总结:

关闭事件的传递。缺省情况下,视图会接收触摸事件。但是,您可以将其
userInteractionEnabled
属性声明设置为
NO
,关闭事件传递的功能。隐藏或透明的视图也不能接收事件。

在一定的时间内关闭事件的传递。应用程序可以调用
UIApplication
beginIgnoringInteractionEvents
方法,并在随后调用
endIgnoringInteractionEvents
方法来实现这个目的。前一个方法使应用程序完全停止接收触摸事件消息,第二个方法则重启消息的接收。某些时候,当您的代码正在执行动画时,可能希望关闭事件的传递。

打开多点触摸的传递。 缺省情况下,视图只接收多点触摸序列的第一个触摸事件,而忽略所有其它事件。如果您希望视图处理多点触摸,就必须使它启用这个功能。在代码或InterfaceBuilder的查看器窗口中将视图的
multipleTouchEnabled
属性设置为
YES
,就可以实现这个目标。

将事件传递限制在某个单独的视图上。 缺省情况下,视图的
exclusiveTouch
属性被设置为
NO
。将这个属性设置为
YES
会使相应的视图具有这样的特性:即当该视图正在跟踪触摸动作时,窗口中的其它视图无法同时进行跟踪,它们不能接收到那些触摸事件。然而,一个标识为“独占触摸”的视图不能接收与同一窗口中其它视图相关联的触摸事件。如果一个手指接触到一个独占触摸的视图,则仅当该视图是窗口中唯一一个跟踪手指的视图时,触摸事件才会被传递。如果一个手指接触到一个非独占触摸的视图,则仅当窗口中没有其它独占触摸视图跟踪手指时,该触摸事件才会被传递。

将事件传递限制在子视图上。一个定制的
UIView
类可以通过重载
hitTest:withEvent:
方法来将多点触摸事件的传递限制在它的子视图上。这个技巧的讨论请参见“事件处理技巧”部分。


处理多点触摸事件

为了处理多点触摸事件,
UIView
的定制子类(比较不常见的还有
UIApplication
UIWindow
的定制子类)必须至少实现一个
UIResponder
的事件处理方法。本文的下面部分将对这些方法进行描述,讨论处理常见手势的方法,并展示一个处理复杂多点触摸事件的响应者对象实例,以及就事件处理的某些技术提出建议。


事件处理方法

在一个多点触摸序列发生的过程中,应用程序会发出一系列事件消息。为了接收和处理这些消息,响应者对象的类必须至少实现下面这些由
UIResponder
类声明的方法之一:

-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event;

-(void)touchesMoved:(NSSet*)toucheswithEvent:(UIEvent*)event;

-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event;

-(void)touchesCancelled:(NSSet*)toucheswithEvent:(UIEvent*)event

在给定的触摸阶段中,如果发生新的触摸动作或已有的触摸动作发生变化,应用程序就会发送这些消息:

当一个或多个手指触碰屏幕时,发送
touchesBegan:withEvent:
消息。

当一个或多个手指在屏幕上移动时,发送
touchesMoved:withEvent:
消息。

当一个或多个手指离开屏幕时,发送
touchesEnded:withEvent:
消息。

当触摸序列被诸如电话呼入这样的系统事件所取消时,发送
touchesCancelled:withEvent:
消息。

上面这些方法都和特定的触摸阶段(比如
UITouchPhaseBegan
)相关联,该信息存在于
UITouch
对象的
phase
属性声明中。

每个与事件处理方法相关联的消息都有两个参数。第一个参数是一个
UITouch
对象的集合,表示给定阶段中新的或者发生变化的触摸动作;第二个参数是一个
UIEvent
对象,表示这个特定的事件。您可以通过这个事件对象得到与之相关联的所有触摸对象(
allTouches
),或者发生在特定的视图或窗口上的触摸对象子集。其中的某些触摸对象表示自上次事件消息以来没有发生变化,或虽然发生变化但处于不同阶段的触摸动作。

为了处理给定阶段的事件,响应者对象常常从传入的集合参数中取得一或多个
UITouch
对象,然后考察这些对象的属性或取得它们的位置(如果需要处理所有触摸对象,可以向该
NSSet
对象发送
anyObject
消息)。
UITouch
类中有一个名为
locationInView:
的重要方法,如果传入
self
参数值,它会给出触摸动作在响应者坐标系统中的位置(假定该响应者是一个
UIView
对象,且传入的视图参数不为
nil
)。另外,还有一个与之平行的方法,可以给出触摸动作之前位置(
previousLocationInView:
)。
UITouch
实例的属性还可以给出发生多少次触碰(
tapCount
)、触摸对象的创建或最后一次变化发生在什么时间(
timestamp
)、以及触摸处于什么阶段(
phase
)。

响应者类并不是必须实现上面列出的所有三个事件方法。举例来说,如果它只对手指离开屏幕感兴趣,则只需要实现
touchesEnded:withEvent:
方法就可以了。

在一个多点触摸序列中,如果响应者在处理事件时创建了某些持久对象,则应该实现
touchesCancelled:withEvent:
方法,以便当系统取消该序列的时候对其进行清理。多点触摸序列的取消常常发生在应用程序的事件处理遭到外部事件—比如电话呼入—破坏的时候。请注意,响应者对象同样应该在收到多点触摸序列的
touchesEnded:withEvent:
消息时清理之前创建的对象(“事件处理技巧”部分讨论了如何确定一个序列中的最后一个touch-up事件)。


处理单个和多个触碰手势

iPhone应用程序中一个很常见的手势是触击:即用户用手指触碰一个对象。响应者对象可以以一种方式响应单击,而以另外一种方式响应双击,甚至可能以第三种方式响应三次触击。您可以通过考察
UITouch
对象的
tapCount
属性声明值来确定用户在一个响应者对象上的触击次数,

取得这个值的最好地方是
touchesBegan:withEvent:
touchesEnded:withEvent:
方法。在很多情况下,我们更倾向于后者,因为它与用户手指离开屏幕的阶段相对应。在触摸结束阶段(
UITouchPhaseEnded
)考察触击的次数可以确定手指是真的触击,而不是其它动作,比如手指接触屏幕后拖动的动作。

程序清单3-1展示了如何检测某个视图上是否发生双击。

程序清单3-1  检测双击手势

-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event

{

UITouch*touch=[touchesanyObject];


if([touchtapCount]==2){

CGPointtapPoint=[theTouchlocationInView:self];

//Processadouble-tapgesture

}

}

当一个响应者对象希望以不同的方式响应单击和双击事件时,就会出现复杂的情况。举例来说,单击的结果可能是选定一个对象,而双击则可能是显示一个编辑视图,用于编辑被双击的对象。那么,响应者对象如何知道一个单击不是另一个双击的起始部分呢?我们接下来解释响应者对象如何借助上文刚刚描述的事件处理方法来处理这种情况:

touchesEnded:withEvent:
方法中,当触击次数为一时,响应者对象就向自身发送一个
performSelector:withObject:afterDelay:
消息,其中的选择器标识由响应者对象实现的、用于处理单击手势的方法;第二个参数是一个
NSValue
NSDictionary
对象,用于保存相关的
UITouch
对象;时延参数则表示单击和双击手势之间的合理时间间隔。

请注意:使用一个
NSValue
对象或字典来保存触摸对象是因为它们会保持传入的对象。然而,您自己在进行事件处理时,不应该对
UITouch
对象进行保持。

touchesBegan:withEvent:
方法中,如果触击次数为二,响应者对象会向自身发送一个
cancelPreviousPerformRequestsWithTarget:
消息,取消当前被挂起和延期执行的调用。如果触碰次数不为二,则在指定的延时之后,先前步骤中由选择器标识的方法就会被调用,以处理单击手势。

touchesEnded:withEvent:
方法中,如果触碰次数为二,响应者会执行处理双击手势的代码。


检测碰擦手势

水平和垂直的碰擦(Swipe)是简单的手势类型,您可以简单地在自己的代码中进行跟踪,并通过它们执行某些动作。为了检测碰擦手势,您需要跟踪用户手指在期望的坐标轴方向上的运动。碰擦手势如何形成是由您自己来决定的,也就是说,您需要确定用户手指移动的距离是否足够长,移动的轨迹是否足够直,还有移动的速度是否足够快。您可以保存初始的触碰位置,并将它和后续的touch-moved事件报告的位置进行比较,进而做出这些判断。

程序清单3-2展示了一些基本的跟踪方法,可以用于检测某个视图上发生的水平碰擦。在这个例子中,视图将触摸的初始位置存储在名为
startTouchPosition
的成员变量中。随着用户手指的移动,清单中的代码将当前的触摸位置和起始位置进行比较,确定是否为碰擦手势。如果触摸在垂直方向上移动得太远,就会被认为不是碰擦手势,并以不同的方式进行处理。但是,如果手指继续在水平方向上移动,代码就继续将它作为碰擦手势来处理。一旦碰擦手势在水平方向移动得足够远,以至于可以认为是完整的手势时,处理例程就会触发相应的动作。检测垂直方向上的碰擦手势可以用类似的代码,只是把x和y方向的计算互换一下就可以了。

程序清单3-2  在视图中跟踪碰擦手势

#defineHORIZ_SWIPE_DRAG_MIN12

#defineVERT_SWIPE_DRAG_MAX4


-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event

{

UITouch*touch=[touchesanyObject];

startTouchPosition=[touchlocationInView:self];

}


-(void)touchesMoved:(NSSet*)toucheswithEvent:(UIEvent*)event

{

UITouch*touch=[touchesanyObject];

CGPointcurrentTouchPosition=[touchlocationInView:self];


//Iftheswipetrackscorrectly.

if(fabsf(startTouchPosition.x-currentTouchPosition.x)>=HORIZ_SWIPE_DRAG_MIN&&

fabsf(startTouchPosition.y-currentTouchPosition.y)<=VERT_SWIPE_DRAG_MAX)

{

//Itappearstobeaswipe.

if(startTouchPosition.x<currentTouchPosition.x)

[selfmyProcessRightSwipe:toucheswithEvent:event];

else

[selfmyProcessLeftSwipe:toucheswithEvent:event];

}

else

{

//Processanon-swipeevent.

}

}


处理复杂的多点触摸序列

触击和碰擦是简单的手势。如何处理更为复杂的多点触摸序列—实际上是解析应用程序特有的手势—取决于应用程序希望完成的具体目标。您可以跟踪所有阶段的所有触摸动作,记录触摸对象中发生变化的属性变量,并正确地改变内部的状态。

说明如何处理复杂的多点触摸序列的最好方法是通过实例。程序清单3-3展示一个定制的
UIView
对象如何通过在屏幕上动画移动“Welcome”标语牌来响应用户手指的移动,以及如何通过改变欢迎标语的语言来响应用户的双击手势(例子中的代码来自一个名为MoveMe的示例工程,进一步考察该工程可以更好地理解事件处理的上下文)。

程序清单3-3  处理复杂的多点触摸序列

-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event

{

UITouch*touch=[[eventallTouches]anyObject];

//Onlymovetheplacardviewifthetouchwasintheplacardview

if([touchview]!=placardView){

//Ondoubletapoutsideplacardview,updateplacard'sdisplaystring

if([touchtapCount]==2){

[placardViewsetupNextDisplayString];

}

return;

}

//"Pulse"theplacardviewbyscalingupthendown

//UseUIView'sbuilt-inanimation

[UIViewbeginAnimations:nilcontext:NULL];

[UIViewsetAnimationDuration:0.5];

CGAffineTransformtransform=CGAffineTransformMakeScale(1.2,1.2);

placardView.transform=transform;

[UIViewcommitAnimations];


[UIViewbeginAnimations:nilcontext:NULL];

[UIViewsetAnimationDuration:0.5];

transform=CGAffineTransformMakeScale(1.1,1.1);

placardView.transform=transform;

[UIViewcommitAnimations];


//MovetheplacardViewtounderthetouch

[UIViewbeginAnimations:nilcontext:NULL];

[UIViewsetAnimationDuration:0.25];

placardView.center=[selfconvertPoint:[touchlocationInView:self]fromView:placardView];

[UIViewcommitAnimations];

}


-(void)touchesMoved:(NSSet*)toucheswithEvent:(UIEvent*)event

{

UITouch*touch=[[eventallTouches]anyObject];


//IfthetouchwasintheplacardView,movetheplacardViewtoitslocation

if([touchview]==placardView){

CGPointlocation=[touchlocationInView:self];

location=[selfconvertPoint:locationfromView:placardView];

placardView.center=location;

return;

}

}


-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event

{

UITouch*touch=[[eventallTouches]anyObject];


//IfthetouchwasintheplacardView,bounceitbacktothecenter

if([touchview]==placardView){

//Disableuserinteractionsosubsequenttouchesdon'tinterferewithanimation

self.userInteractionEnabled=NO;

[selfanimatePlacardViewToCenter];

return;

}

}

请注意:对于通过描画自身的外观来响应事件的定制视图,在事件处理方法中通常应该只是设置描画状态,而在
drawRect:
方法中执行所有的描画操作。如果需要了解更多关于描画视图内容的方法,请参见“图形和描画”部分。


事件处理技巧

下面是一些事件处理技巧,您可以在自己的代码中使用。

跟踪UITouch对象的变化

在事件处理代码中,您可以将触摸状态的相关位置保存下来,以便在必要时和变化之后的
UITouch
实例进行比较。作为例子,假定您希望将每个触摸对象的最后位置和其初始位置进行比较,则在
touchesBegan:withEvent:
方法中,您可以通过
locationInView:
方法得到每个触摸对象的初始位置,并以
UITouch
对象的地址作为键,将它们存储在
CFDictionaryRef
封装类型中;然后,在
touchesEnded:withEvent:
方法中,可以通过传入
UITouch
对象的地址取得该对象的初始位置,并将它和当前位置进行比较(您应该使用
CFDictionaryRef
类型,而不是
NSDictionary
对象,因为后者需要对其存储的项目进行拷贝,而
UITouch
类并不采纳
NSCopying
协议,该协议在对象拷贝过程中是必须的)。

对子视图或层上的触摸动作进行触碰测试

定制视图可以用
UIView
hitTest:withEvent:
方法或
CALayer
hitTest:
方法来寻找接收触摸事件的子视图或层,进而正确地处理事件。下面的例子用于检测定制视图的层中的“Info”
图像是否被触碰。

-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event{

CGPointlocation=[[touchesanyObject]locationInView:self];

CALayer*hitLayer=[[selflayer]hitTest:[selfconvertPoint:locationfromView:nil]];


if(hitLayer==infoImage){

[selfdisplayInfo];

}

}

如果您有一个携带子视图的定制视图,就需要明确自己是希望在子视图的级别上处理触摸事件,还是在父视图的级别上进行处理。如果子视图没有实现
touchesBegan:withEvent:
touchesEnded:withEvent:
、或者
touchesMoved:withEvent:
方法,则这些消息就会沿着响应者链被传播到父视图。然而,由于多次触碰和多点触摸事件与发生这些动作所在的子视图是互相关联的,所以父视图不会接收到这些事件。为了保证能接收到所有的触摸事件,父视图必须重载
hitTest:withEvent:
方法,并在其中返回其本身,而不是它的子视图。

确定多点触摸序列中最后一个手指何时离开

当您希望知道一个多点触摸序列中的最后一个手指何时从视图离开时,可以将传入的集合参数中包含的
UITouch
对象数量和
UIEvent
参数对象中与该视图关联的触摸对象数量相比较。请看下面的例子:

-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event{

if([touchescount]==[[eventtouchesForView:self]count]){

//lastfingerhaslifted....

}

}

看完文档我们也许还不能完全掌握ios触摸事件,下面我们结合一个实例来继续学习。

新建一个项目SwitchByGesture,添加一个UIViewController命名为SecondViewController。

在ViewController.m中添加下面代码:

#pragmamark-

-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event
{
UITouch*touch=[touchesanyObject];
gestureStartPoint=[touchlocationInView:self.view];
}
-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event
{
for(UITouch*touchintouches){
CGPointcurrentPosition=[touchlocationInView:self.view];
CGFloatdeltaX=currentPosition.x-self.gestureStartPoint.x;
CGFloatdeltaY=fabsf(currentPosition.y-self.gestureStartPoint.y);
if(deltaX<=-25&&deltaY<=10){
SecondViewController*secondView=[[SecondViewControlleralloc]init];
secondView.title=@"SecondLevel";
[self.navigationControllerpushViewController:secondViewanimated:YES];
}

}
}


我们实现了touchBegan方法,在该方法中我们记住触摸事件其实坐标,在touchEnded方法中我们获取手势结束时的坐标,接着判断是否符合我们的条件(即水平向左滑动且上下滑动不超过10),如果符合切换视图。

接着同样我们要在SecondViewController.m中实现上述两个方法只不过判断条件不同如下:

#pragmamark-
-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event
{
UITouch*touch=[touchesanyObject];
gestureStartPoint=[touchlocationInView:self.view];
}
-(void)touchesEnded:(NSSet*)toucheswithEvent:(UIEvent*)event
{
for(UITouch*touchintouches){
CGPointcurrentPosition=[touchlocationInView:self.view];
CGFloatdeltaX=currentPosition.x-self.gestureStartPoint.x;
CGFloatdeltaY=fabsf(currentPosition.y-self.gestureStartPoint.y);
if(deltaX>=25&&deltaY<=10){
[self.navigationControllerpopViewControllerAnimated:YES];
}

}
}


即水平向右滑动,切换原来的视图。编译运行,如果不出意外的话,会出现下面的效果:

向左滑动

 



向右滑动



好了就写这么多,有什么问题请留言,大家一起学习交流!

说明:转载请注明出处!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息