IOS和OSX事件传递机制

本文ios部分转载自:html

http://zhoon.github.io/ios/2015/04/12/ios-event.htmlios

iOS的事件有好几种:Touch Events(触摸事件)、Motion Events(运动事件,好比重力感应和摇一摇等)、Remote Events(远程事件,好比用耳机上得按键来控制手机),其中最经常使用的应该就是Touch Events了,基本存在于每一个app的每一个地方,今天咱们主要就讲讲它,至于其余两个事件有兴趣的能够自行查阅资料。git

在网页上当咱们讲到事件,咱们会讲到事件响应链,咱们会讲到事件的响应者和事件的传递方式(冒泡),那么在app上,其实也离不开这几个问题,今天咱们也重这几个方面来介绍iOS的事件机制: 一、响应链是何时怎样构建的? 二、事件第一个响应者是怎么肯定的? 三、事件第一个响应者肯定后,系统是怎样传递事件的?github

  • 响应链的构建

不管是哪一种事件,其传递和响应都与响应链息息相关,那么响应链究竟是一个什么样的东西呢? 在UIKit中有一个类:UIResponder,咱们能够看看头文件的几个属性和方法:app

UIResponder是全部能够响应事件的类的基类(从名字应该就能够看出来了),其中包括最多见的UIView和UIViewController甚至是UIApplication,因此咱们的UIView和UIViewController都是做为响应事件的载体。ide

那么响应链跟这个UIResponder有什么关系呢?事实事件响应链的造成和事件的响应和传递,UIResponder都帮咱们作了不少事。咱们的app中,全部的视图都是按照必定的结构组织起来的,即树状层次结构,每一个view都有本身的superView,包括controller的topmost view(controller的self.view)。当一个view被add到superView上的时候,他的nextResponder属性就会被指向它的superView,当controller被初始化的时候,self.view(topmost view)的nextResponder会被指向所在的controller,而controller的nextResponder会被指向self.view的superView,这样,整个app就经过nextResponder串成了一条链,也就是咱们所说的响应链。因此响应链就是一条虚拟的链,并无一个对象来专门存储这样的一条链,而是经过UIResponder的属性串连起来的。以下图:函数

  • Hit-Testing View

文章开头说到有iOS三种event类型,事件传递中UIWindow会根据不一样的event,用不一样的方式寻找initial object,initial object决定于当前的事件类型。好比Touch Event,UIWindow会首先试着把事件传递给事件发生的那个view,就是下文要说的hit-testview。对于Motion和Remote Event,UIWindow会把例如震动或者远程控制的事件传递给当前的firstResponder,有关firstResponder的相关信息请看这里。下面主要讲Touch Event的hit-testview。spa

有了事件响应链,接下来的事情就是寻找响应事件的具体响应者了,咱们称着为:Hit-Testing View,寻找这个View的过程咱们称着为Hit-Test。code

那么什么是Hit-Test呢,咱们能够把它理解为一个探测器,经过这个探测器咱们能够找到并判断手指是否点击在某个视图上面,换句话说就是经过Hit-Test能够找到手指点击到的处于屏幕最前面的那个UIView。htm

在解释Hit-Test是怎么工做以前,先来看看它是何时被调用的。前面说Hit-Test是一个探测器,那么在代码里面其实就是一个函数,UIView有以下两个方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; 
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

每当手指接触屏幕,UIApplication接收到手指的事件以后,就会去调用UIWindow的hitTest:withEvent:,看看当前点击的点是否是在window内,若是是则继续依次调用subView的hitTest:withEvent:方法,直到找到最后须要的view。调用结束而且hit-test view肯定以后,这个view和view上面依附的手势,都会和一个UITouch的对象关联起来,这个UITouch会做为事件传递的参数之一,咱们能够看到UITouch头文件里面有一个view和gestureRecognizers的属性,就是hitTest view和它的手势。

如今知道Hit-Test是何时调用了,那么接下来看看它是怎么工做的。Hit-Test是采用递归的方法从view层级的根节点开始遍历,看看下面这张图:

UIWindow有一个MianVIew,MainView里面有三个subView:view A、view B、view C,他们各自有两个subView,他们层级关系是:view A在最下面,view B中间,view C最上(也就是addSubview的顺序,越晚add进去越在上面),其中view A和view B有一部分重叠。若是手指在view B.1和view A.2重叠的上面点击,按照上面说的递归方式,顺序以下图所示:

递归是向界面的根节点UIWindow发送hitTest:withEvent:消息开始的,从这个消息返回的是一个UIView,也就是手指当前位置最前面的那个 hittest view。 当向UIWindow发送hitTest:withEvent:消息时,hitTest:withEvent:里面所作的事,就是判断当前的点击位置是否在window里面,若是在则遍历window的subview而后依次对subview发送hitTest:withEvent:消息(注意这里给subview发送消息是根据当前subview的index顺序,index越大就越先被访问)。若是当前的point没有在view上面,那么这个view的subview也就不会被遍历了。当事件遍历到了view B.1,发现point在view B.1里面,而且view B.1没有subview,那么他就是咱们要找的hittest view了,找到以后就会一路返回直到根节点,而view B以后的view A也不会被遍历了。

一图胜千言:

注意hitTest里面是有判断当前的view是否支持点击事件,好比userInteractionEnabled、hidden、alpha等属性,都会影响一个view是否能够相应事件,若是不响应则直接返回nil。 咱们留意到还有一个pointInside:withEvent:方法,这个方法跟hittest:withEvent:同样都是UIView的一个方法,经过他开判断point是否在view的frame范围内。若是这些条件都知足了,那么遍历就能够继续往下走了,代码表现大概以下:

======================================================================

  • OSX事件响应连

osx的事件响应大致上跟ios同样的,可是有重要的差异:当事件遍历到了view B.1,发现point在view B.1里面,而且view B.1没有subview,所以它就是咱们要找的hittest view 了。可是hittest并不会中止,会继续遍历view A,viewA2也是咱们要找的hitestview。遍历如图所示:

 

 

mousedown事件响应的顺序为:

相关文章
相关标签/搜索