一、 事件的分发
1、当点击屏幕的时候iOS设备的硬件会检测(检测的原理可以去查找资料,好像触摸屏分为电容屏和电阻屏,然后根据一些他们的特性来检测的)到这个触摸事件,然后打包到UIEvent对象防盗应用程序的事件分发队列里面(队列:先进先出的特点,所有先进去的先执行)。UIApplication对象从队列里面取出最先进去的那个事件然后进行分发。一般会先传递给Window 。
解释下这两个方法:
方法1
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法2
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
当系统在调用第一个方法时会自动调用第二个方法,可以看出,第二个方法是判断point那个点是不是在调用第一个方法的视图上,如果是则方法1会返回当前这个视图,否则返回nil。事件的处理是根据返回的视图来执行响应的处理方法的,等下讲解关于事件链的传递,现在先讲事件的分发。
来看看实例:UIWindow->myview->view1、view2
在window上面有一个子视图myview,在my view视图上有两个子视图view1,view2
假如说我们点击了view2视图。
分析点击view2之后事件的分发流程:
在点击了view2之后 系统检测到这个事件,然后放在了事件的队列里面,当uiapplication对象取出事件的时候向window发送消息,此时会调用window里面的
(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 方法 ( 编号:方法1)
在调用此方法的时候会在方法里面调用- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法 (编号: 方法2)
当方法2判断出point(触摸屏幕的手指的位置)这个点在window上面 ,则会向直接加载到window上的视图myiew发送消息,同时执行过程和上面一样。先调用方法1.同时在方法1里面调用方法2,检测到point那个点是在myview上,则会向所有的子视图也就是view1和view2发送消息,然后执行相同的操作。
注意:在向所有的子视图发送消息的时候会先给top层那个发送(每个视图上的子视图的存放都是放在数组里面的,先添加进去的下标小,后添加的大)。因为,假如有多个视图是重合在一起的情况,检测到的点肯定都是在这些重合的视图上的,但是返回那个视图呢?一般我们只处理顶层那个我们看的到到视图的响应,这个顶层点视图肯定是最后添加上去的,所有在向所有的子视图发送消息时会先发送给顶层那个,这样假如有重合的情况下 在重叠的下面视图就不必发送消息了,这样效率会高。 我们也可以自定义方法1 来返回指定的视图。
例如:在一个视图view上有一个button和一个scrollview重叠,button在下面,scrollview在顶层。我们触摸这个位置的时候方法1默认返回的是视图scrollview,但是我们可以自定义让它返回button视图
我们在view这个视图点implement里面重写方法1:
(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//事件拦截 如果view有这些条件限制 则返回nill
if (self.alpha < 0.01 || self.userInteractionEnabled == NO || self.hidden == YES) {
return nil;
}
//判读触摸点是不是在这个视图里面 如果是 则遍历子视图 button和scrollview 系统默认的是先向top层的发送消息,然后返回它,这里我们判断出这个点在重叠的区域的时候让它返回button
if ([self pointInside:point withEvent:event]) {
NSArray *subViews = self.subviews;
//从顶层开始遍历 因为如果有重叠的 情况 一般在下面层的 控件事件我们 不需要处理 只处理顶层那个
for (NSInteger i = subViews.count - 1; i >= 0; i--) {
UIView *view = subViews[i]; //第一次循环的时候 view = scrollview
// 将触摸点转化到scrollview 视图上
CGPoint convertPoint = [self convertPoint:point toView:view];
//这句比较关键 在重叠的情况下 系统默认的返回时top层那个视图 这里也就是scrollview
UIView *view1 = [view hitTest:convertPoint withEvent:event];
return view1;//系统估计在这里就直接返回top视图了 但是我们要返回的不是这个 因此下面我们改变返回的视图
if (view1 != nil) {
return subviews[i-1];//返回button
break;
}
}
else
return nill;
}
这里我们 的事件的分发就讲完了,实际上就是不断的查找,直到找到触摸的那个视图。
关于响应者链欢迎点击下一篇博客(由于篇幅的原因写在下一篇)