您的位置:首页 > 移动开发 > IOS开发

iOS Hit-Testing事件响应链

2016-06-06 21:19 357 查看

iOS Hit-Testing事件响应链

官方文档Understanding Event Handling, Responders, and the Responder Chain中,有这样的叙述:

app使用响应者对象接收和处理事件。响应者对象是
UIResponder
类的实例,常见的子类包括
UIView
UIViewController
UIApplication
。响应者接收原始事件数据,并且必须处理该事件或将其转发给另一个响应者对象。当你的app收到一个事件时,
UIKit
会自动将该事件指向最合适的响应者对象,称为第一响应者(first responder)。

未被处理的事件会从响应者传递到活动的响应者链(active responder chain)中的响应者,响应者链是应用程序的响应者对象的动态配置(Unhandled events are passed from responder to responder in the active responder chain, which is a dynamic configuration of your app’s responder objects)

在你的app中没有单个响应者链。
UIKit
为对象如何从一个响应者传递到另一个响应者定义了默认的规则,但是你可以通过覆盖响应者对象中相应的属性来改变这些规则。

下图显示了一个应用程序中默认的响应者链,其接口包含label,text field,一个button和两个背景 view。如果text field不处理事件,则UIKit将该事件发送到文本字段的父级
UIView
对象,然后是该window的根view。在根view中,响应者链在将事件引导到window之前转移到拥有该view的view controller。如果窗口不处理事件,则UIKit将事件传递给
UIApplication
对象,如果该对象是
UIResponder
的实例并且已经不是响应者链的一部分,则可能传递给app delegate。



对于每种类型的事件,
UIKit
都会指定一个第一响应者,并首先将该事件发送给该对象。 第一响应者根据事件的类型而变化。

Touch events

第一响应者是触摸发生的view

Press events

第一个响应者是焦点的响应者

Shake-motion events

第一个响应者是你(或者UIKit)指定的第一个响应者

Remote-control events

第一个响应者是你(或者UIKit)指定的第一个响应者

Editing menu messages

第一个响应者是你(或者UIKit)指定的第一个响应者

Controls使用action消息直接与其关联的目标对象进行通信。 当用户与控件进行交互时,控件调用其目标对象的动作方法 - 换句话说,它向其目标对象发送一个动作消息。 Action消息不是事件,但它们仍然可以利用响应者链。 当控件的目标对象为
nil
时,UIKit从目标对象开始,遍历响应者链,直到找到实现相应操作方法的对象。 例如,UIKit编辑菜单使用此行为来搜索响应者对象,这些对象实现了名称为
cut(_ :)
copy(_ :)
paste(_ :)
的方法

如果view附加有的gesture recognizer,则gesture recognizer在view接收之前接收touch和press事件。 如果view的所有的gesture recognizer都无法识别其手势,则将事件传递给view进行处理。 如果view不处理它们,则UIKit将事件传递给响应者链。 有关使用手势识别器处理事件的更多信息,请参阅处理UIKit手势。

确定哪个响应者包含触摸事件

UIKit
使用基于视图的hit-testing来确定触摸事件发生的位置。 具体来说,
UIKit
将触摸位置与视图层次结构中视图对象的边界进行比较。
UIView
hitTest(_:with :)
方法遍历视图层次结构,查找包含指定触摸的最深的子视图。 该view成为触摸事件的第一响应者。

注意

如果触摸位置超出view边界,则
hitTest(_:with :)
方法会忽略该view及其所有子view。 因此,当一个视图的
clipsToBounds
属性为
false
时,即使这些子视图碰巧包含触摸,也不会返回该view边界外的子视图。

UIKit
将每个触摸永久分配给包含它的视图。
UIKit
在触摸第一次出现时创建每个
UITouch
对象,并且只有在触摸结束后才释放该触摸对象。 随着触摸位置或其他参数的更改,
UIKit
会使用新信息更新
UITouch
对象。 唯一不变的属性是包含视图(containing view)。 即使触摸位置移动到原始视图之外,触摸视图属性中的值也不会改变。

hitTest(_:with:)

hitTest(_:with:)
返回包含指定点receiver的视图层级(包括它本身)中的最深的后代。(Returns the farthest descendant of the receiver in the view hierarchy (including itself) that contains a specified point.)

此方法通过遍历视图层级,调用每个子视图的
point(inside:with:)
方法来确定哪个子视图应该接收触摸事件。如果
point(inside:with:)
返回
true
,那么类似的遍历子视图的视图层次,直到找到包含指定点的最前面的view。如果视图不包含该点,则该视图层级的分支将被忽略。

此方法忽略隐藏的视图对象、禁用了用户交互,或者alpha级别小于0.01的view

如下的例子,button是有颜色的view的子view,但是button在view的边界之外,此时点击button,并不会响应button的事件



可以这样重写
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法,来使button响应action事件:

重写有颜色view的
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *view = [super hitTest:point withEvent:event];
if (view) {
return view;
}
for (UIView *subView in self.subviews) {
CGPoint cPoint = [self convertPoint:point toView:subView];
if ([subView hitTest:cPoint withEvent:event]) {
return subView;
}
}
return nil;
}


其它应用包括:

参考:

iOS事件响应链中Hit-Test View的应用

Hit-Testing in iOS

iOS hitTest-点击事件分发分析

1.扩大响应的区域

2.将触摸事件传递给下面的视图

3.将触摸事件传递给子视图
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios hit-test