在iOS开发中会遇到一些设计样式,需要把按钮一部分悬空在父视图的上面,但是当我们点击该按钮时,超出了父视图的悬空部分不会响应该按钮的点击事件。
大概如图所示:
红色容器View1中有蓝色button 需要在View1视图外点击button仍然生效。关于响应者链的学习资料:
iOS响应者链、事件的传递
iOS响应者链彻底掌握
这篇博客中比较详细的描述了响应者链是怎样传递的以及Hit-Test 机制 是如何生效的。我就不描述其中的原理了,我们这边只分析代码:
- (void)viewDidLoad
{
UIView1 *view1 = [[UIView1 alloc] initWithFrame:CGRectMake(0, 0, 260, 100)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(120, 50, 260, 100)];
btn.backgroundColor = [UIColor blueColor];
[btn addTarget:self action:@selector(btnClick)
forControlEvents:UIControlEventTouchUpInside];
[view1 addSubview:btn];
}
- (void)btnClick
{
NSLog(@"btn 被点击了");
}
在UIView1中重写 hitTest 方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(@"View1 hitTest start ");
UIView *item = [super hitTest:point withEvent:event];
if (item == nil) { // 超出屏幕外的部分
__block UIButton *btn = nil;
// 先检查subviews 里的视图
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[UIButton class]]) {
// 找到需要处理的btn
btn = (UIButton *)obj;
*stop = YES;
}
}];
// 当找到了对应的处理btn
if (btn) {
// 转换坐标系
CGPoint p = [self convertPoint:point toView:btn];
// 判断触摸点是否在button上
if (CGRectContainsPoint(btn.bounds, p)) {
item = [btn hitTest:p withEvent:event];
}
}
}
NSLog(@"View1 hitTest - %@",item);
return item;
}
在点击超出原视图外的部分时,判断该点上有没有响应的子视图,如果没有,就再判断一下该点是不是在点击的这个按钮的视图坐标范围内,如果是响应该点击事件。