前言
最近App增加新功能,需要在高德地图上进行点击标注并增加气泡。气泡二次点击进行导航。按照文档集成后,把imageView改成button。结果发现无法处理点击事件。经过分析,应该是气泡是基于标注添加的。而标注在地图中范围特别小,气泡的范围超出了标注的范围。众所周知,超过父视图范围的button是不能响应点击事件的。
解决原理
解决这个问题,首先想到了hitText: withEvent:方法。下面具体说一下hitText:
withEvent方法使用。iOS系统检测到Touch操作时会将其放入当前活动Application的事件队
列,Application会从事件队列中取出触摸事件并传递给当前接收用户事件的窗口处
理,window对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视
图(View),即需要将触摸事件传递给其处理的视图,称之为hit-test view。
hitText: withEvent:方法的处理流程如下:
1.首先调用当前视图的pointInside: withEvent:方法(该方法用来判断点击事件发生的
位置是否处于当前视图范围内),若返回NO,则hitTest:withEvent:返回nil。若返回YES,则向
当前视图所有子视图发送hitTest:withEvent:消息。遍历的的顺序是从subviews数组中从末
尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。
2.若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束,
如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。
解决问题
这里我们可以知道,在处理视图穿透,或者点击下层视图即可用到hitText: withEvent:
方法。故我们CustomAnnotationView.m中重写hitText: withEvent:方法。并在其中把接收点
坐标系转换成指定坐标系。(这也是网上的统一答案)。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//由上可知,点击点不在当前视图中,故必然返回nil
UIView *view = [super hitTest:point withEvent:event];
if (view == nil) {
//在这里我们把点击点从CustomAnnotationView坐标系转换成点击按钮的坐标系
CGPoint tempoint = [self.calloutView.navBtn convertPoint:point fromView:self];
//判断转换后的点tempoin是否处于点击按钮的rect中,注意,在使用此方法时,两个参数必须属于同一坐标系中
if (CGRectContainsPoint(self.calloutView.navBtn.bounds, tempoint))
{
view = self.calloutView.navBtn;
}
}
//返回的即为可以接收点击事件的点击按钮
return view;
}
当然,也可以简洁一点:
- (UIView*)hitTest:(CGPoint)pointwithEvent:(UIEvent*)event {
CGPoint tempoint = [self.calloutView.navigationButton convertPoint:point fromView:self];
if ([self.calloutView.navigationButton pointInside:tempoint withEvent:event]) {
return self.calloutView.navigationButton;
}else{
return[superhitTest:pointwithEvent:event];
}
}
延伸思考
如果我们要实现一个view覆盖一个button,点击button时处理button的点击事件,点击
其余view范围,处理view自己的事件。同样也要用到hitTest:withEvent:方法。
解决如下:
- (UIView*)hitTest:(CGPoint)pointwithEvent:(UIEvent*)event {
//先把坐标系从self转换到button
CGPoint touchPoint = [self.btn convertPoint:point fromView:self];
//如果点击点在按钮上,返回按钮。如果不在,返回本身相应
if ([self.btn pointInside:touchPoint withEvent:event]) {
return self.btn;
}else{
return [superhitTest:point withEvent:event];
}
}