触摸事件<响应者链>

这篇博文是学习iOS以来的第一篇博文,是对自己学习的一个总结,亦是给大家的一个参考学习,我会尽可能的写得通俗易懂,易于大家理解。

1.iOS中的事件

在iOS中事件大致分为三种:触摸事件,加速计事件,远程控制事件。

2.iOS中的事件,

2.1概念

接下来要讨论一下响应者对象的概念,什么是响应者对象。在iOS中不是任何对象都能处理事件,只有继承了UIResponder得对象才能接受并处理事件,我们称之为“响应者对象”。UIApplication,UIViewController,UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件。

2.2UIResponder

UIResponder内部提供了以下一些方法来处理事件:

2.2.1触摸事件

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event;

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event;

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event;

2.2.2加速计事件

- (void)motionBegan:(UIEventSubtype)motionwithEvent:(UIEvent *)event;

- (void)motionEnded:(UIEventSubtype)motionwithEvent:(UIEvent *)event;

- (void)motionCancelled:(UIEventSubtype)motionwithEvent:(UIEvent *)event;

2.2.3远程控制事件

- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

2.3接下来说一下我们开发常用的UIView的触摸事件处理

2.3.1UIViewUIResponder的子类,可以覆盖下列4个方法处理不同的触摸事件<Tips:touches中存放的都是UITouch对象>

一根或者多根手指开始触摸view,系统会自动调用view的下面方法

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;

一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)

- ( void)touchesMoved:( NSSet*)touches withEvent:( UIEvent*)event

一根或者多根手指离开view,系统会自动调用view的下面方法

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event


触摸结束前,某个系统事件会打断触摸过(比如突然一个电话打进来等等),系统会自动调用view的下面方法
- ( void)touchesCancelled:( NSSet*)touches withEvent:( UIEvent *)event
2.3.2接下来我觉得有必要讲解下touches和event对象
UITouch的概念:
当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象。一根手指会对应一个UITouch对象。
UITouch作用:
保存着跟手指相关的信息,比如触摸的位置、时间、阶段。
当手指移动时,系统会更新同一个 UITouch对象,使之能够一直保存该手指在的触摸位置
当手指离开屏幕时,系统会销毁相应的 UITouch对象
UITouch的属性详解:
触摸产生时所处的窗口

@property ( nonatomic , readonly , retain ) UIWindow   *window;

触摸产生时所处的视图
@property( nonatomic, readonly, retain) UIView    *view;   短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击

@property(nonatomic,readonly)NSUInteger    tapCount; 记录了触摸事件产生或变化时的时间,单位是秒

@property(nonatomic,readonly)NSTimeInterval    timestamp; 当前触摸事件所处的状态

@property(nonatomic,readonly)UITouchPhase        phase;  


UITouch的方法:

- (CGPoint)locationInView:(UIView*)view;

返回值表示触摸在view上的位置,这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0)),

调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置

- ( CGPoint)previousLocationInView:( UIView*)view;
该方法记录了前一个触摸点的位置
UIEvent概念:
每产生一个事件,就会产生一个 UIEvent 对象
UIEvent :称为事件对象,记录事件产生的 时刻 类型

UIEvent属性:

事件类型

@property(nonatomic,readonly)UIEventType     type;
@property(nonatomic,readonly)UIEventSubtype  subtype;
事件产生的时间
@property(nonatomic,readonly)NSTimeInterval  timestamp;
UIEvent方法:

UIEvent还提供了相应的方法可以获得在某个view上面的触摸对象(UITouch
2
.3.2touches和event参数
一次完整的触摸过程,会经历3个状态:

触摸开始:- ( void)touchesBegan:( NSSet*)touches withEvent:( UIEvent *)event
触摸移动:- ( void )touchesMoved:( NSSet *)touches withEvent:( UIEvent *)event
触摸结束:- ( void )touchesEnded:( NSSet *)touches withEvent:( UIEvent *)event
触摸取消( 可能会经历 ):- ( void )touchesCancelled:( NSSet *)touches withEvent:( UIEvent *)event
4个触摸事件处理方法中,都有 NSSet *touches和 UIEvent *event两个参数
一次完整的触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数
如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个 UITouch 对象
如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个 UITouch 对象

根据touches中 UITouch的个数可以判断出是单点触摸还是多点触摸
2.4讨论过事件处理的一些常用方法和属性,这里就要从宏观上说一下事件的产生和传递的过程
发生触摸事件后,系统会将该事件加入到一个由 UIApplication管理的事件队列中, UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow),主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步,找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理,touchesBegan,touchesMoved,touchedEnded, touchesCancelled。
触摸事件的传递是从父控件传递到子控件,比如举一个例子,一个控制器view上面有两个子view,view1包含view2.这样当我们触摸view2的时候,时间传递是这样的, UIApplication->UIWindow->view->view1->view2.
还有在这里需要注意的是 如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件。
那么在这里还要讨论一下UIView不处理触摸事件的三种情况:

1>不接受用户交互userInteractionEnabled =NO<例如:UIImageViewuserInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的
>

2>隐藏 hidden = YES
3>透明
alpha = 0.0 ~ 0.01

3.接下来要讨论本文的重点响应者链条
触摸事件处理的详细过程,当用户点击屏幕后产生的一个触摸事件,经过一些列的传递过程后,会找到最合适的视图控件来处理这个事件,找到最合适的视图控件后,就会调用控件的touches方法来作具体的事件处理,touchBegan等等,这些touches方法的默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理,交给上一个相应者进行处理,是指将touches传递给它的父view,一直向上传递。过程顺序 (1)如果view的控制器存在,就传给控制器,如果控制器不存在,则再传递给它的俯视图(2)在试图层次结构的最顶级试图,如果也不能处理收到的事件或消息,则将事件或消息传递给window对象进行处理.(3)如果window也不能进行处理,则将事件或消息传递给UIApplication对象.(4)如果UIApplication也不能进行如理,则将事件和消息丢弃。

接下来为大家做一个Demo,供大家更好的消化。

新建一个工程最中间的黑色view名叫view1,白色view名叫view2,蓝色view名叫view3,他们三个没有class继承关系,只是视图摆放上为继承关系。图1.

三个view视图各自对应自己的试图控制器UIView,

view1对应控制器代码如下图2


view2对应控制器内部代码touchesBegan为空,不做重写。

view3对应控制器内部代码如下图3

按照这样的代码填写运行之后,点击黑色view1,view2,view3,会各自是什么结果呢?大家可以思考一样,做个猜测,再看下面结果是否有出入。

正确结果是这样的:

点击黑色view1变为是红色,点击白色view2变为绿色,点击蓝色view3变为绿色。那么为什么呢?解释一下。

当点击view1的时候,进入view1控制器触摸事件touchBegan:方法,运行到[super touchesBegan:...],会调用父view的touchBegan方法,将事件处理传递给下一个响应者也就是父view2的touchBegan方法,父view2内部这个方法没有重写,不做任何操作改变,如果没有重写操作系统默认会将事件传递给下一个响应者,这里会在传递给view3的控制器touchBegan方法,先执行[super touchesBegan:...](做下提示,这里会再条用下一个响应者的方法,以致window,UIApplication)再执行[[[touchesanyObject]view]setBackgroundColor:[UIColorgreenColor]];执行到这里view1已经变为绿色的了,为什么可以拿到view1呢,因为touches集合类型,内部装的只有一个对象,就是点击的那个view1,先传递给view2控制器,再传递给view3,touches始终装着第一次点击的那个view。变为绿色之后,程序又再执行到view1控制器的第28行,再将view1置为红色,覆盖之前的绿色<友情提示:打上断点测试过程,印象会更清晰>。

点击view2会变为绿色,道理一样,都是传递本view给下一个响应者<也就是view3>,置为绿色,不过这里面view2因为没有重写控制器代码,所以没有覆盖绿色。

点击view3会变为绿色,同上,先执行[super touchesBegan:...],不过执行无实际操作,之后再执行view3代码的第25行,置为绿色。

代码[super touchesBegan:...]和设置操作代码顺序变换,会出现不同的结果,根据实际项目需要而来。例如本例子:如下图2,图3

图2

此时,出现的结果会变为点击三个view1,view2,view3都是绿色。具体原因,执行顺序不同,响应者链的处理过程原理是和上面一致的。


本文讲述的是关于事件处理的知识,其实在iOS3.2之后,苹果又推出的事实识别功能(Gesture Recognizer),在触摸事件处理方面,大大简化了开发者的开发难度,非常好用的功能,不过事件有限,在这里先不多做阐述,以后有时间再写关于手势的博文。如果本文写的有不足之处,请大家谅解也欢迎大家指正,第一篇博文,多多包涵哈@~@


你可能感兴趣的:(多点触摸,事件处理,触摸屏,响应者链,UIResponder)