ios-事件响应者链

来源:互联网 发布:熟练掌握编程 英文 编辑:程序博客网 时间:2024/04/30 15:45

对于IOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕、晃动设备、通过遥控设施控制设备。对应的事件类型有以下三种:

1、触屏事件(Touch Event)

2、运动事件(Motion Event)

3、远端控制事件(Remote-Control Event)

响应者链条概念: iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。

响应者对象(Responder Object) 指的是 有响应和处理事件能力的对象。 响应者链就是由一系列的响应者对象 构成的一个层次结构。

UIResponder 是所有响应对象的基类,在UIResponder类中定义了处理上述各种事件的接口。我们熟悉的 UIApplication、 UIViewController、 UIWindow 和所有继承自UIView的UIKit类都直接或间接的继承自UIResponder,所以它们的实例都是可以构成响应者链的响应者对象。

UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,此方法会在其视图层级结构中的每个视图上调用pointInside:withEvent:(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是要找的hit-test view。

     无论是哪种事件,都是系统本身先获得,是iOS系统来传给UIApplication的,由Application再决定交给谁去处理,所以如果我们要拦截事件,可以在UIApplication层面或者UIWindow层面去拦截。

        


hitTest:withEvent:方法的处理流程如下: 
首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内; 
若返回NO,则hitTest:withEvent:返回nil; 
若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕; 
若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束; 
如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。 

假如用户点击了View E,下面结合图二介绍hit-test view的流程:

1、A是UIWindow的根视图,因此,UIWindwo对象会首相对A进行hit-test;

2、显然用户点击的范围是在A的范围内,因此, pointInside:withEvent:返回了YES,这时会继续检查A的子视图;

3、这时候会有两个分支,B和C:

点击的范围不再B内,因此B分支的 pointInside:withEvent:返回NO,对应的hitTest:withEvent:返回nil;

点击的范围在C内,即C的 pointInside:withEvent:返回YES;

4、这时候有D和E两个分支:

点击的范围不再D内,因此D 的 pointInside:withEvent:返回NO,对应的hitTest:withEvent:返回nil;

点击的范围在E内,即E的 pointInside:withEvent:返回YES,由于E没有子视图(也可以理解成对E的子视图进行hit-test时返回了nil),因此,E的hitTest:withEvent: 会将E返回,再往回回溯,就是C的 hitTest:withEvent:返回E--->>A的 hitTest:withEvent:返回E。 

至此,本次点击事件的第一响应者就通过响应者链的事件分发逻辑成功的找到了。

不难看出,这个处理流程有点类似二分搜索的思想,这样能以最快的速度,最精确地定位出能响应触摸事件的UIView。

***上面找到了事件的第一响应者,接下来就该沿着寻找第一响应者的相反顺序来处理这个事件,如果UIWindow单例和UIApplication都无法处理这一事件,则该事件会被丢弃。***

说明:

1、如果最终 hit-test没有找到第一响应者,或者第一响应者没有处理该事件,则该事件会沿着响应者链向上回溯,如果UIWindow实例和UIApplication实例都不能处理该事件,则该事件会被丢弃;

2、hitTest:withEvent:方法将会忽略隐藏(hidden=YES)的视图,禁止用户操作(userInteractionEnabled=YES)的视图,以及alpha级别小于0.01(alpha<0.01)的视图。如果一个子视图的区域超过父视图的bound区域(父视图的clipsToBounds 属性为NO,这样超过父视图bound区域的子视图内容也会显示),那么正常情况下对子视图在父视图之外区域的触摸操作不会被识别,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。当然,也可以重写pointInside:withEvent:方法来处理这种情况。

3、我们可以重写 hitTest:withEvent:来达到某些特定的目的,下面的链接就是一个有趣的应用举例,当然实际应用中很少用到这些。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孕38周有点贫血怎么办 脚被热水烫红了怎么办 学员练车撞死人怎么办 三胎意外来了该怎么办 因为飞机延误耽误行程怎么办 伤了朋友的心怎么办 羊蹄被绳子缠肿了怎么办 新老师教的不好怎么办 跟老公三观不合怎么办 突然有社保补扣怎么办 街头篮球篮板反应慢怎么办 换水了龙鱼顶缸怎么办 压着眼睛睡觉醒来模糊怎么办 天热眼睛有眼屎怎么办 眼睛里膜起来了怎么办 眼睛一边大一边小怎么办 眼白膜鼓起来了怎么办 主持问答环节没人提问怎么办 转学原学校不给怎么办 村长借东西不还怎么办 村长不上报建房申请怎么办 村长不上报建房手续申请怎么办 村长不给村民盖章怎么办? 找村干部办事难怎么办 洪洞县村长不给我盖章怎么办 睾丸穿刺取精只配到6个胚胎怎么办 孩子一只耳朵听不到声音怎么办 孕早期孕囊生长慢怎么办 试管2次不着床怎么办? pescm球员年龄大了怎么办 实况足球俱乐部经理球员老了怎么办 你不是我的菜怎么办 苹果平板电脑耳机有回音怎么办 obs直播有电流音怎么办 语音里网吧很吵怎么办 电脑k歌有延迟怎么办 想开个跆拳道馆怎么办营业执照? 壶嘴拐弯处漏水怎么办 裂纹茶壶嘴坏了怎么办 小孩刚上学怕她上火怎么办 在幼儿园小朋友不愿叠衣服怎么办