UITouchEvent事件分发机制

2016-05-12  本文已影响159人  Smicro
Smicro

根据右边的图来简要说一下hitTest检测流程:

view接收到hitTest消息会通过自己调用pointInSize:withEvent:来判断该点是不是在自己内部。下面以(Judge)来表示view通过该方法的判断结果。

触点1--A(Judge = YES)--B(Judge = NO)、D(Judge = NO)、E(Judge = NO)、F(Judge = NO)--A

触点2--A(Judge = YES)--B(Judge = YES)--C(Judge = YES)--C

触点3--A(Judge = YES)--B(Judge = NO)      

                                          --E相对D在顶部,优先遍历E(Judge = YES)--E

触点4--不在A内--nil

归纳:

hitTest:会首先在 application 的 keyWindow 上调用(UIWindow 也是 UIView 的子类),并且该方法的返回值将被用来处理事件。会先判断产生触摸的 point 是否发生在自己的 bounds 内,如果没有将返回 nil;如果 point 在自己的范围内,则会为自己的每个子视图调用 hitTest: 方法,只要有一个子视图通过这个方法返回一个 UIView 对象,那么整个方法就一层一层地往上返回;如果没有子视图返回 UIView 对象,则父视图将会把自己返回。

补充:

如果某个view的enabled或userInteractionEnable为NO或者alpha<0.01,那么该view的hitTest永远(pointInSize:withEvent:返回YES/NO)都会返回nil,这意味着它和它的子视图没有机会去接收和处理事件。

在自定义控件时,我们经常要响应一个并非是矩形的区域,那么这时候,就可以通过重写View的pointInSize:withEvent:来限制响应区域了。

默认的写法:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {

return CGRectContainsPoint(self.bounds, point);

}

现在想将范围限定在一个圆形内:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {

BOOL inside = [self.superview pointInside:point withEvent:event];

if (inside) {

CGFloat radius = self.frame.size.width / 2;

CGFloat dx = point.x - radius;

CGFloat dy = point.y - radius;

CGFloat distace = sqrt(dx * dx + dy * dy);

return distace < radius;

}

return inside;

}

附:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

if (self.alpha <= 0.01 || !self.userInteractionEnabled || self.hidden) {

return nil;

}

BOOL inside = [self pointInside:point withEvent:event];

UIView *hitView = nil;

if (inside) {

NSEnumerator *enumerator = [self.subviews reverseObjectEnumerator];

for (UIView *subview in enumerator) {

if (hitView) {

break;

}

}

if (!hitView) {

hitView = self;

}

return hitView;

} else {

return nil;

}

}

上一篇下一篇

猜你喜欢

热点阅读