检测点击是在自定义 calloutView 内部还是外部

Posted

技术标签:

【中文标题】检测点击是在自定义 calloutView 内部还是外部【英文标题】:Detect if tap is inside or outside custom calloutView 【发布时间】:2020-04-03 09:08:47 【问题描述】:

我有一张带有一些图钉的地图。当我单击 Pin 时,自定义标注显示为 UIView。当我在 calloutView 内点击时,什么都不会发生。如果我在视图外点击,自定义 calloutView 应该会消失。但我不知道如何实现它,要么它在视图内外的水龙头处消失,要么根本不消失。

我处理水龙头的函数:

    func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) 
    if view == customCallout 
        return
     else 
        customCallout?.removeFromSuperview()
    

在这种情况下,它会在两次点击时删除自定义标注。我在这里创建标注:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) 
    if view.annotation is MKUserLocation 
        return
    
    //this creates the callout
    let views = Bundle.main.loadNibNamed("CustomCalloutView", owner: nil, options: nil)
    let calloutView =  views?[0] as! CustomCalloutView
    calloutView.delegate = self
    customCallout = calloutView

如果我在自定义标注视图内部点击,它不会消失,但如果我在外部点击,它会消失,我该如何实现?

【问题讨论】:

【参考方案1】:

旧的Location and Maps Programming Guide 概述了该过程。该技术涉及为您的注释视图实现hitTest 方法,该方法包括自定义标注中的命中。否则,触摸不会被识别为注释视图和/或标注的一部分,而是在地图上。

正如Guide 所说:

当您使用自定义视图而不是标准标注时,您需要做额外的工作以确保您的标注在用户与其交互时正确显示和隐藏。以下步骤概述了创建包含按钮的自定义标注的过程:

    设计代表自定义标注的 NSView 或 UIView 子类。子类很可能需要实现 drawRect: 方法来绘制您的自定义内容。 创建一个视图控制器,用于初始化标注视图并执行与按钮相关的操作。 在注解视图中,实现 hitTest: 以响应在注解视图范围之外但在标注视图范围内的命中,如清单 6-7 所示。 在注释视图中,实现 setSelected:animated: 以在用户单击或点击时将标注视图添加为注释视图的子视图。如果用户选择时标注视图已经可见,则 setSelected: 方法应从注释视图中移除标注子视图(参见清单 6-8)。 在注解视图的 initWithAnnotation: 方法中,将 canShowCallout 属性设置为 NO 以防止在用户选择注解时地图显示标准标注。 清单 6-7 显示了一个实现 hitTest 的示例:处理标注视图中可能超出注释视图边界的命中。

清单 6-7 响应自定义标注中的点击

- (NSView *)hitTest:(NSPoint)point

    NSView *hitView = [super hitTest:point];
    if (hitView == nil && self.selected) 
        NSView *calloutView = self.calloutViewController.view;
        NSPoint pointInCalloutView = [self convertPoint:point toView:calloutView];
        hitView = [calloutView hitTest:pointInCalloutView];
    
    return hitView;

清单 6-8 显示了一个实现 setSelected:animated: 的示例,以在用户选择注释视图时为自定义标注视图的到达和解除设置动画。

清单 6-8 添加和删除自定义标注视图

- (void)setSelected:(BOOL)selected

    [super setSelected:selected];

    // Get the custom callout view.
    NSView *calloutView = self.calloutViewController.view;
    if (selected) 
        NSRect annotationViewBounds = self.bounds;
        NSRect calloutViewFrame = calloutView.frame;
      // Center the callout view above and to the right of the annotation view.
        calloutViewFrame.origin.x = -(NSWidth(calloutViewFrame) - NSWidth(annotationViewBounds)) * 0.5;
        calloutViewFrame.origin.y = -NSHeight(calloutViewFrame) + 15.0;
        calloutView.frame = calloutViewFrame;

        [self addSubview:calloutView];
     else 
        [calloutView.animator removeFromSuperview];
    

现在,上面显然是 Objective-C(清单 6-8 的实现可能与他们的不同),但在 Swift 中基本模式是相同的。请参阅这个 CustomMapViewAnnotationCalloutSwift Github 存储库,它说明了如何在 Swift 中实现自定义注释视图(包括这个所需的 hitTest 行为)。因此,它结合了在注释视图中实现 hitTest,以及显然让标注消耗这些触摸。

【讨论】:

以上是关于检测点击是在自定义 calloutView 内部还是外部的主要内容,如果未能解决你的问题,请参考以下文章

高德 ios 自定义气泡添加点击事件无效问题

UIButton在Annotation的calloutView上点击失败

在自定义表格单元格内检测多个 UI 视图的点击/触摸的理想方法是啥?

Vue.js 自定义指令使用 TypeScript 检测点击外部元素

iOS 自定义 .xib 视图按钮点击检测在视图控制器 (Swift)

FakeXposed最强屏蔽XposedRoot检测,自定义maps文件重定向等支持Android5~11