hitTest: withEvent: 的作用:
- 寻找并返回最合适的view,即,能够响应事件的那个最合适的view;
- 拦截事件的处理:想让谁成为最合适的view,就重写其父控件的
hitTest:withEvent:
方法返回指定的子控件;
假如用户点击了View E,结合上面最经典的图介绍hit-test view的流程:
A 是UIWindow的根视图,因此,UIWindwo对象会首先对 A 进行hit-test;
显然用户点击的范围是在 A 的范围内,因此,
pointInside:withEvent:
返回了YES,这时会继续检查 A 的子视图;这时候会有两个分支,B 和 C :
点击的范围不在 B 内,因此 B 分支的pointInside:withEvent:
返回NO,对应的hitTest:withEvent:
返回nil;
点击的范围在 C 内,即 C 的pointInside:withEvent:
返回YES;这时候有 D 和 E 两个分支:
点击的范围不在 D 内,因此 D 的pointInside:withEvent:
返回NO,对应的hitTest:withEvent:
返回nil;
点击的范围在E内,即 E 的pointInside:withEvent:
返回YES,由于 E 没有子视图(也可以理解成对E的子视图进行hit-test时返回了nil),因此,E 的hitTest:withEvent:
会将 E 返回,再往回回溯,就是 C 的hitTest:withEvent:
返回E--->>A的hitTest:withEvent:
返回 E 。
至此,本次点击事件的第一响应者就通过响应者链的事件分发逻辑成功的找到了。
总结:
事件的传递顺序: 就是寻找最合适 view 的过程。
产生触摸事件 -> UIApplication事件队列 -> [UIWindow hitTest:withEvent:] -> 返回更合适的view -> [子控件 hitTest:withEvent:] -> 返回最合适的view。即:触摸事件的传递是从父控件传递到子控件。
事件的响应: 首先看 initial View 能否响应这个事件,如果不能则会将事件传递给其上级视图(superView);如果上级视图仍然无法处理,则会继续往上传递;一直传递到视图控制器:判断视图控制器的 self.view
能否处理这个事件,若果不能就判断该视图控制器能否处理,如果还不能处理就继续往上传递,一直到 window
;如果 window
还不能处理就交给 application
处理,如果 application
还是不能处理,就将其丢弃。
看一下实际运用的例子:
说明:两个灰色的button:LeftButton 和 RightButton,放到他们的父视图粉红色的view上。按常理来说,点击
框一
或框二
的位置,由于已经超出了父视图了,不会响应button的点击事件。
下面是单独对LeftButton处理,之后我们点击框一
位置就可以响应LeftButton的响应事件了,具体请看代码。
#import "PinkView.h"
@interface PinkView ()
@property (weak, nonatomic) IBOutlet UIButton *leftButton;
@property (weak, nonatomic) IBOutlet UIButton *rightButton;
@end
@implementation PinkView
#pragma mark - Private Methods
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 如果父视图
// `self.userInteractionEnabled == NO`
// `self.hidden == YES` 或 `self.alpha < 0.01`,那么就不响应事件
if (!self.userInteractionEnabled || self.hidden || self.alpha < 0.01) {
return nil;
}
// 将点击的位置转化为相对于 `self.leftButton` 的坐标
CGPoint leftButtonPoint = [self convertPoint:point toView:self.leftButton];
// 判断转化后的坐标,是否在 `self.leftButton`内
if ([self.leftButton pointInside:leftButtonPoint withEvent:event]) {
return self.leftButton;
}
// 其他的触摸事件仍交由系统处理
return [super hitTest:point withEvent:event];
}
- (IBAction)onLeftButton:(id)sender {
NSLog(@"onLeftButton");
}
- (IBAction)onRightButton:(id)sender {
NSLog(@"onRightButton");
}
@end