iOS开发者大都略知一二事件的传递机制,但是对于事件在处理过程中所调用的具体方法和方法所应用的场景是比较模糊的.今天我就简单的介绍一下事件处理流程和具体的使用.
事件处理流程:
1.当用户点击屏幕时会产生一个触摸事件,系统会将该事件包装成一个UIEvent加入到一个由UIApplication管理的事件队列中.
2.UIApplication会从事件队列中取出最前面的事件进行分发以便处理,通常先发送给应用程序的主窗口(UIWindow)
3.主窗口会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到最适合的UIView来处理触摸事件.(hitTest:withEvent其实也是UIView的一个方法,UIWindow继承自UIView,因此主窗口UIWindow也是属于视图的一种)
hitTest:withEvent:方法大致处理流程时这样的:
调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:
- 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:方法返回nil
- 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的子控件(subViews),然后调用子控件的hitTest:withEvent:和pointInside:withEvent:方法来判断触摸点是否在某个子控件上.子视图的遍历顺序时top到bottom,即从subViews数组的末端向前遍历,直到有子视图的hitTest:withEvent:方法返回为非空对象或者全部子视图遍历完毕.
- 若第一次有子视图的hitTest:withEvent:返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束
- 若所有的子视图的hitTest:withEvent:方法返回nil,则当前视图的hitTest:withEvent:返回当前视图自身(self).
4.最终,这个触摸事件交给主窗口的hitTest:withEvent方法返回的视图对象去处理.
举例:
1.扩大UIButton的响应区域(上下左右各增加10)
重写UIButton的pointInside:withEvent:方法
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(CGRectInset(self.bounds, -10, -10), point)) {
return YES;
}
return NO;
}
2.子控件超出父控件的bounds如何响应事件
一般超出部分是不会响应事件的,所以我们要重写父控件的pointInside:withEvent:方法来处理
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL flag = NO;
for (UIView *view in self.subviews) {
if (CGRectContainsPoint(view.frame, point)){
flag = YES;
break;
}
}
return flag;
}