iOS事件机制

响应者链

我们的视图结构是一个N叉树(一个视图可以有多个子视图,一个子视图同一时刻只有一个父视图),而每一个继承UIResponder的对象都可以在这个N叉树中扮演一个节点。当叶节点成为最高响应者的时候,从这个叶节点开始往其父节点开始追朔出一条链,那么对于这一个叶节点来讲,这一条链就是当前的响应者链,而这一个节点就是这条响应链的hit-test view。响应者链将系统捕获到的由UITouch(触摸)形成的UIEvent(事件)从叶节点开始层层向下分发,期间可以选择停止分发,也可以选择继续向下分发。

分发工作是由- (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event方法负责的。响应者通过重写该方法截获触摸事件,触摸事件一旦被截获,默认就不再往下分发。若想继续分发,在方法中调用父类的该方法即可。需要注意的是响应触摸事件的其余3个方法都不具备分发触摸事件的功能,因此若没有在touchesBegan方法中继续往下分发触摸事件,而在另外3个方法中对父类调用了对应的方法,这样的做法是无效的(所谓无效就是父类的该方法不被调用),因为触摸事件根本就没有传递下去,而UIResponser在调用触摸事件的响应方法前会先识别是否有触摸事件分发到自己。

另外,触摸事件在一个时刻只能被一个UIResponder截获。例如在一个view上添加了两个subView,两个subView有重叠部分,触摸重叠部分,由上层的subView截获触摸事件,而不是两个subView都截获。

理解gesture recoginzer和触摸事件分发的关系:

gesture recoginzer是被绑定在view上的,不参与响应链。

当一个触摸事件发生的时候,触摸事件被放入事件队列中,事件从app(UIApplication)开始(因此默认app是第一响应者)传递,app根据先进先出的原则将触摸事件从事件队列中取出来,首先传递给window(UIWindow)。window之后的下一个响应者对象是hit-test view,这里有一个寻找hit-text view的过程,称为hit-testing。但是将触摸事件传递(分发)给hit-test view之前,window会先将事件交给hit-test view或其父视图上绑定的gesture recognizer。这里有个手势识别的过程,有两点需要注意:
(1)识别从hit-test view上绑定的gesture recognizer开始,一直到hit-test view所在的最底层的视图,所有视图上的gesture recognizer都会去识别触摸事件并处于识别待定(UIGestureRecognizerStatePossible)状态,但是父视图上的gesture recognizer必须等子视图的gesture recognizer识别完成(识别成功或失败)了再改变的识别状态(同样是识别成功还是识别失败),也就是说子视图有优先识别权。一旦某个gesture recognizer识别成功,触摸事件就被该gesture recognizer截获,由该gesture recognizer响应触摸事件,其余的gesture recognizer不再识别触摸事件。因此,hit-text view到其根部的view上最多只有一个gesture recognizer能截获然后响应该触摸事件;
(2)默认情况下,在gesture recognizer识别触摸事件时,触摸事件会同时分发给hit-test view。当触摸事件被截获,即某个gesture recognizer成功识别了触摸事件后,系统会发送touchesCancelled:withEvent消息来取消hit-test view对触摸事件的处理,至此触摸事件终止。若所有gesture recognizer都识别失败,触摸事件才完全由hit-test view掌控,接下来的触摸事件的分发就按响应链传递,直到触摸事件被某个响应者对象截获并停止往下分发或者所有响应者对象都不能截获触摸事件,触摸事件终止。

gesture recoginzer的3个控制触摸事件分发的属性

cancelsTouchesInView默认为YES,表示当GestureRecognizers识别到手势后,会向hit-test view发送 touchesCancelled:withEvent:消息来取消hit-test view对此触摸事件的处理,这样只有GestureRecognizers能响应此触摸事件,hit-test view不再响应。如果为NO,则不发送touchesCancelled:withEvent:消息给hit-test view,这样会使Gesture Recognizers和hit-test view同时响应触摸序列。

delaysTouchesBegan默认为NO,表示触摸事件开始时,而手势识别器还未识别出此手势时(即处于识别待定状态时),触摸事件会同时发向hit-testview,这样在手势识别器还未识别出此手势,hit-testview同时也可以收到同样的触摸事件。如果为YES,则在手势识别器识别手势的过程中,不会有任何触摸事件发送给hit-test view,如果手势识别器最终识别到了手势,则也不会发送任何消息(包括touchesCancelled:withEvent:)给hit-test view(因为根本就没begin);若所有的手势器最终没有识别到手势,则所有的触摸事件再发给hit-test view处理。需要注意的是,由于触摸事件在分发给hit-test view之前会分发给hit-test view及其父视图上所有绑定的gesture recognizer识别,因此若其中有一个gesture recognizer设置了delaysTouchesBegan为yes,触摸事件就不会分发给hit-test view。

delaysTouchesEnded默认为YES。若在系统即将发送touchEnd消息的时候,gesture recognizer既没有识别成功,也没有识别失败,可以通过该属性控制是否发送touchEnd消息。举个例子,一个双击手势,单击后gesture recognizer开始识别,同时触摸事件发送给了hit-test view,在gesture recognizer等待第二次点击的时候(这时候即没有识别成功,也没有识别失败),hit-test view对于触摸事件响应完成准备发送touchEnd消息。此时若delaysTouchesEnded为YES,hit-test view会等到gesture recognizer识别完成再决定是否发送(识别失败则发送,识别成功则发送touchCancel);若delaysTouchesEnded为NO,则立即发送。

delaysTouchesEnded为YES时双击:

delaysTouchesEnded为NO时双击:

参考文档:
《iOS事件机制(一)》
《Gesture Recognizers与触摸事件分发》
《Responder一点也不神秘————iOS用户响应者链完全剖析》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值