本章探讨:产生触摸事件,到处理的整个过程以及对事件的控制
第一部分:找出处理事件的对象
-
当产生一个触摸的事件时,会首先进入UIApplication的事件收集队列;
-
队列保持先进先出原则
-
首先将最开始的事件发送给UIWindow
-
UIWindow调用hitTest:withEvent:
hitTest:withEvent:方法大致处理流程是这样的:
首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:
若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil
若pointInside:withEvent:方法返回YES,则遍历subviews; 调用子视图的hitTest:withEvent:(子视图遍历顺序,从top到bottom)
当第一次有子视图的hitTest:withEvent:返回非空对象时,当前视图的hitTest:withEvent:就返回此对象,处理结束
当所有子视图都返回空对象时,当前视图的hitTest:withEvent:返回当前视图本身(self)
最终这个事件就交给主窗口hitTest:withEvent:方法返回的对象去处理。
第二部分:利用相应链对事件进行传递,控制,扩散
要点:
-
iOS的消息处理是,当消息被人处理后默认不再向父层传递
-
iOS判断哪个界面能接受消息是从View层级结构的父View向子View传递,即树状结构的根节点向叶子节点递归传递
-
hitTest和pointInside成对,且hitTest会调用pointInside。
响应链顺序:
UIApplicationDelegate <- UIWindow <- UIViewController <- UIView <- UIView
1、实例:将消息传递给下一个响应者
#pragma mark - touches
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event
{
NSLog(@"B - touchesBeagan..");
// 把事件传递下去给父View或包含他的ViewController
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent *)event
{
NSLog(@"B - touchesCancelled..");
// 把事件传递下去给父View或包含他的ViewController
[self.nextResponder touchesBegan:touches withEvent:event];
}
...
self.nextResponder通过重写touchs系列方法,实现对事件的处理
2、重写hitTest拦截消息
#pragma mark - hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 当touch point是在_btn上,则hitTest返回_btn
CGPoint btnPointInA = [_btn convertPoint:point fromView:self];
if([_btn pointInside:btnPointInA withEvent:event]) {
return_btn;
}
// 否则,返回默认处理
return[superhitTest:point withEvent:event];
}
3、阻止view对事件的响应:
- 1、设置View.UserInteractionEnable=NO
- 2、override重写pointInside
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// 本View不响应用户事件
returnNO;
}