原文地址:https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/understanding_event_handling_responders_and_the_responder_chain
如原作者发现有侵权行为可责令我在24小时之内删除,前提是你能看到。
翻译者:毛毛可
学习事件是怎样在你的app中传递的,并学会应该如何处理它们.
Apps使用responder(响应者对象)对象来接受并处理事件.一个responder对象可以是任意一个UIResponder类的实例,其中包括其子类UIView,UIViewController和UIApplication.
responder对象在接收到原始的事件对象,必须处理它或者将它继续向前传递给下一个responder对象.当你的app在接收到一个事件时,UIKit框架会自动的找到最合适的responder去处理该事件,这就是第一响应者.
未处理的事件会在响应者链中从这个responder传递给下一个responder.事实上你的app中没有单独的响应者链.UIkit定义了responder传递的默认规则,当然你可以通过覆盖responder对象中对应的属性来更改规则.
图一 显示了一条默认的响应者链,包括一个label,一个text field,一个button和两个background view.如果text field不去处理事件,那么UIKit会将事件发送给textfield的父视图对象.接着就是window的root view.从root view出来,会顺着响应者链在到达window对象之前先传递给view controller.如果此时window也不处理此事件,UIKit会将事件分发给UIApplication对象,或者是分发给app delegate,app delegate是一个UIResponder实例,但此过程已经不是响应者链中的一部分了.
确定事件的第一响应者
针对每种类型的事件,UIKit指定了第一响应者,将事件发送给第一响应者.第一响应者的确定是基于事件的类型的.
Touch事件
第一响应者就是触摸发生的view.
Press事件
第一响应者是注册了focus的响应者对象.
摇动事件
第一响应者是你指定的相应对象.
远程控制事件
第一响应者是你指定的相应对象.
编辑菜单信息
第一响应者是你指定的相应对象.
Note
运动事件相关的加速,陀螺仪和磁力计都不会出现在响应者链中.Core MOtion会派发这些事件给你对应注册的对象.
UIControl控件会直接通过相关联的target-action传送事件.
当界面中的是UIControl控件时,UIControl会调用target对象的action方法,或者说返送一个action消息给其target对象.
action消息不是一个事件,但是依然可以利用响应者链.当UIControl对象的target为nil时,UIKit开始从target对象并顺着响应者链找,直到找到一个实现了相关action方法的对象.举例,UIKit editing menu使用这种行为去搜索对应responder对象的相关实现方法比如cut:
,copy:
或者paste:
.
如果在view中有附加的手势识别器,手势识别器会在在view之前接收到touch和press事件.如果view中所有的手势识别都没有识别成功,事件会传给view去处理.如果此view没有处理它们,UIKit会继续传给响应者链的上一层.关于更多的手势识别器处理事件,请参考UIKit Gestures.
确定哪个Responder包含Touch事件
UIKit使用 基于view的hit-testing方式去确定touch事件发生的位置.具体来说,UIKit会将touch的位置与在view层级中的view对象的容器范围比较.UIView的hitTest:withEvent:
方法随着view层级,查找包含touch的最深层的子view.这个view将成为touch事件的第一响应者.
Note
如果touch的位置超出了view的范围,hitTest:withEvent:
方法会忽略此view及其所有子view.因此,当view的clipsToBounds属性为NO时,即使包含了touch,超出view范围的子view也不会有效.更多关于hitTest:withEvent:
方法的行为,请查看view的hitTest:withEvent:
方法.
UIkit总会赋值给view其范围范围的每个touch.当触摸发生时,UIKit创建UITouch对象,直到触摸结束后才释放touch对象.如果触摸的位置或者其他参数改变了,UIKit会及时更新UITouch对象的信息.只有一个属性不可能发生改变就是是否在view范围中.及时当触摸位置移出了原view,touch对象的属性也不会改变.
改变响应者链
你可以通过重载responder对象的nextResponder属性来改变响应者链.你可以在此方法(getter)返回下一个响应者.
许多UIKit的类已经重载了这个属性,返回了指定的对象.
UIView对象,如果view是view controll的根视图,那么view的下个响应者就是view controller,否者下一个响应者就是view的父视图.
UIViewController对象.
如果一个vc是被另一个vcpresent出来的,那么vc的下一个响应者就是present它出来的那个vc
如果window的根视图是view controller.view,那么view controller的下一个响应者就是window对象.
window对象.window的下一个responder是UIApplication对象.
UIApplication对象.下一个响应者是app delegate.