You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击MKAnnotationView时UIMenuController无法显示的技术问题

解决MKAnnotationView选中时UIMenuController不显示的问题

嘿,我来帮你搞定这个问题!你代码里的didSelect方法确实触发了,但菜单没显示主要是两个核心问题没处理到位:目标视图没成为第一响应者,以及菜单的目标矩形设置错了。UIMenuController的显示逻辑依赖于目标视图是第一响应者,而且你之前把目标区域设成了整个地图的frame,菜单可能跑到看不见的地方去了。

下面是修正后的完整方案:

1. 自定义MKAnnotationView子类

系统默认的MKAnnotationView不能成为第一响应者,所以我们需要自定义一个子类,重写相关属性:

class CustomAnnotationView: MKAnnotationView {
    // 允许视图成为第一响应者
    override var canBecomeFirstResponder: Bool {
        return true
    }
    
    // 过滤支持的菜单动作,也可以在ViewController里统一处理
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return action == #selector(ViewController.deletePointer)
    }
}

2. 在地图中使用自定义视图

确保你的地图在创建标注视图时使用这个自定义类:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    guard !(annotation is MKUserLocation) else { return nil }
    
    let reuseId = "CustomAnnotation"
    var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? CustomAnnotationView
    if annotationView == nil {
        annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        // 可自定义标注图标
        annotationView?.image = UIImage(named: "your-annotation-icon")
    } else {
        annotationView?.annotation = annotation
    }
    
    return annotationView
}

3. 修正didSelect方法

调整菜单的目标矩形,并确保标注视图成为第一响应者:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    print("selectedview")
    
    // 让标注视图成为第一响应者,这是菜单显示的前提
    guard view.becomeFirstResponder() else {
        print("标注视图无法成为第一响应者")
        return
    }
    
    let menuController = UIMenuController.shared
    let deleteItem = UIMenuItem(title: "Usun", action: #selector(deletePointer))
    menuController.menuItems = [deleteItem]
    
    // 设置菜单的目标区域为标注视图的范围,确保菜单显示在标注附近
    menuController.setTargetRect(view.bounds, in: view)
    menuController.setMenuVisible(true, animated: true)
}

@objc func deletePointer() {
    print("deleted")
    // 添加删除标注的逻辑
    if let selectedAnnotation = mapView.selectedAnnotations.first {
        mapView.removeAnnotation(selectedAnnotation)
    }
    // 操作完成后隐藏菜单
    UIMenuController.shared.setMenuVisible(false, animated: true)
}

4. 在ViewController中支持菜单动作检查

为了让菜单动作能被正确响应,需要在ViewController里重写canPerformAction方法:

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    if action == #selector(deletePointer) {
        return true
    }
    return super.canPerformAction(action, withSender: sender)
}

关键原因说明

  • 第一响应者:UIMenuController需要一个第一响应者来接收事件,系统默认的标注视图不具备这个能力,所以必须自定义子类开启。
  • 目标矩形:之前用mapView.frame会让菜单基于整个地图定位,大概率不在可视区域;改成view.bounds后,菜单会紧贴着选中的标注显示,用户能直接看到。
  • 动作检查:重写canPerformAction是为了告诉系统当前控制器可以处理这个菜单动作,避免菜单项失效。

内容的提问来源于stack exchange,提问作者kunass2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:50:48