iOS开发之事件的产生和传递

来源:互联网 发布:红包自动抢软件 编辑:程序博客网 时间:2024/05/29 15:58

之前在做画板应用的时候,我们使用了三个方法:

- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent *)event- (void)touchesMoved:(NSSet<UITouch*> *)touches withEvent:(UIEvent *)event- (void)touchesEnded:(NSSet<UITouch*> *)touches withEvent:(UIEvent *)event

其实这些方法是触摸事件的方法,其中还有一个方法:

-(void)touchesCancelled:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event

还记得我们使用这些方法干了什么吗?

我们使用这些方法实现了手指在view 上的触摸事件处理。touchesBegan知道手指开始触摸的位置;touchesMoved知道手指移动过程中的位置;touchesEnded还知道手指触摸结束时候的位置,以此推断touchesCancelled方法可以告诉我们手指触摸被中止(如来电)时候的位置。

 

本章将介绍触摸事件的处理过程。

先了解一些和触摸事件有关的知识

1)用户在使用app的过程中,会产生各种各样的事件。iOS中的事件分为3大类型(引用官当文档的图片):

         触摸事件                             加速计事件                   远程控制器事件

2)响应者对象:iOS中不是任何对象都能处理事件,只有继承自UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。

UIApplication、UIController、UIView都是继承自UIResponder,因此它们都是响应者对象,都能接受并处理事件。

3)UIResponder:为什么继承自UIResponder就能接受并处理事件呢。

因为UIResponder内部提供了以下方法来专门处理事件:

触摸事件方法:

- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent *)event- (void)touchesMoved:(NSSet<UITouch*> *)touches withEvent:(UIEvent *)event- (void)touchesEnded:(NSSet<UITouch*> *)touches withEvent:(UIEvent *)event-(void)touchesCancelled:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event

加速计事件方法:

- (void)motionBegan:(UIEventSubtype)motionwithEvent:(nullable UIEvent *)event- (void)motionEnded:(UIEventSubtype)motionwithEvent:(nullable UIEvent *)event- (void)motionCancelled:(UIEventSubtype)motionwithEvent:(nullable UIEvent *)event

远程控制事件方法

- (void)remoteControlReceivedWithEvent:(nullableUIEvent *)event

这里我们主要介绍触摸事件,所以对触摸事件方法做一下简单说明。拿之前自定义画板的例子。

画板是继承自UIView,UIView是继承自UIResponder。可以覆盖触摸方法来处理不同的事件:

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

-(void)touchesBegan:(NSSet<UITouch *>*)touches withEvent:(UIEvent *)event

一根或多根手指在view上移动,系统会自动调用下面方法(随着手指的移动,系统会一直调用这个方法)

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

一根或多根手指离开view,系统会自动调用下面方法(触摸事件结束)

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

一根或多根手指在view上的触摸被中止(如来电),系统会自动调用下面方法

-(void)touchesCancelled:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event

注意:touches中存放的都是UITouch对象

现在了解关于UITouch的知识:

1、当用户用一根触摸屏幕时,会创建一个与手指相关联的UITouch对象

2、一根手指对应一个UITouch对象

UITouch的作用

1、保存着跟手指相关的信息,比如触摸的位置、时间、阶段

2、当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置

3、当手指离开屏幕时,系统会销毁相应的UITouch对象

提示:iPhone开发中,要避免使用双击事件。

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;

1.返回值表示触摸在view上的位置

2.这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))

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

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

1.该方法记录了前一个触摸点的位置

再来了解一下UIEvent的相关知识:

1、         每产生一个事件,就会产生一个UIEvent对象。

2、          UIEvent:称为事件对象,记录事件产生的时刻和类型。

常见属性:

1)        事件类型

@property(nonatomic,readonly)UIEventType type;@property(nonatomic,readonly)UIEventSubtype subtype;

2)        事件产生的时间

@property(nonatomic,readonly)NSTimeInterval timestamp;

UIEvent还提供了相应的方法可以获得在某个view上面的触摸对象

 

了解了UITouch和UIEvent,现在来说说触摸方法中的两个参数:

一次完整的触摸过程,会经历3个状态:

1、触摸开始:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

2、触摸移动:- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

3、触摸结束:- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

4、触摸取消(可能会经历):- (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的个数可以判断出是单点触摸还是多点触摸

对UITouch和UIEvent有所了解,那下面我们就可以来讲解事件的产生和传递。

•       发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中

•       UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)

•       主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步

•       找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理

         1、touchesBegan…

         2、touchesMoved…

         3、touchedEnded…

观察下图:


说明:

1:控制器的view

2:控制器view的子控件

3:橙色2的子控件

4:蓝色3的子控件


触摸事件的传递是从父控件传递到子控件。

点击了绿色的view:

UIApplication -> UIWindow -> 白色 -> 绿色

点击了蓝色的view:

UIApplication -> UIWindow -> 白色 -> 橙色 -> 蓝色

点击了黄色的view:

UIApplication-> UIWindow -> 白色 -> 橙色 -> 蓝色 -> 黄色

如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件。

那么是如何找到这个最适合的控件来处理事件的呢?它的原则如下:

1、          判断自己是否能接收触摸事件;

2、          判断触摸点是否在自己身上;

3、          从后往前遍历子控件,重复前面的两个步骤;

4、          如果没有符合条件的子控件,那么自己就是最适合的view,自己处理事件

那么view有哪些情况是不能接收触摸事件的呢?

1、          不接收用户交互

userInteractionEnabled= NO

2、          隐藏

hidden = YES

3、          透明

alpha = 0.0 ~0.01

提示:UIImageViewuserInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的

 

用户点击屏幕后产生的一个触摸事件,经过一些列的传递过程后,会找到最合适的视图控件来处理这个事件。找到最合适的视图控件后,就会调用控件的touches方法来作具体的事件处理

touchesBegan…

touchesMoved…

touchedEnded…

这些touches方法的默认做法(调用[super touches])是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理(响应者链条:由多个响应者对象通过事件传递连接在一起的链条。之前提到过响应者对象:能够接受并处理事件的对象(UIResponder))

引用官方文档的图:

响应者链的事件传递过程:

  1. 如果view的控制器存在,就传递给控制器;如果控制器不存在,则将其传递给它的父视图
  2. 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
  3. 如果window对象也不处理,则其将事件或消息传递给UIApplication对象
  4. 如果UIApplication也不能处理该事件或消息,则将其丢弃

大家可以简单记一下如何找到view的下一个响应者:

1)        如果当前view是控制器的view,那么控制器就是下一个响应者。

2)        如果当前view不是控制器的view,那么就父控件就是下一个响应者。

触摸事件的产生和传递就介绍到这里,大家主要记住和了解的就是事件的传递响应者链条

0 0
原创粉丝点击