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

MapView自定义Annotation未始终生效问题排查

自定义MKMarkerAnnotationView缩放后变回默认红色大头针问题

我参考示例实现了带蓝色背景白色水滴样式的自定义标注,选择继承MKMarkerAnnotationView而非MKAnnotationView,目的是复用默认标注的交互行为(比如点击时放大)。但遇到了问题:缩放或平移地图后,自定义蓝色水滴标注偶尔会变成默认的红色大头针,多次缩放后几乎所有标注都会变成红色大头针。

问题演示动图

原代码

ViewController.swift

import UIKit
import MapKit

class ViewController: UIViewController {

    let coordinates = [
        CLLocationCoordinate2D(latitude: 37.3105042, longitude: -122.0380024),
        CLLocationCoordinate2D(latitude: 37.3099890, longitude: -122.0404633),
        CLLocationCoordinate2D(latitude: 37.3573748, longitude: -122.0692252),
        CLLocationCoordinate2D(latitude: 37.3285758, longitude: -122.0787571),
        CLLocationCoordinate2D(latitude: 37.3311494, longitude: -122.0588439),
        CLLocationCoordinate2D(latitude: 37.3261889, longitude: -122.0626754),
        CLLocationCoordinate2D(latitude: 37.3578807, longitude: -122.0535947),
        CLLocationCoordinate2D(latitude: 37.3497026, longitude: -122.0562993),
        CLLocationCoordinate2D(latitude: 37.3403821, longitude: -121.9736835),
        CLLocationCoordinate2D(latitude: 37.3766856, longitude: -122.0302783),
        CLLocationCoordinate2D(latitude: 37.3656187, longitude: -122.0378590),
        CLLocationCoordinate2D(latitude: 37.3655657, longitude: -122.0374650),
        CLLocationCoordinate2D(latitude: 37.3242644, longitude: -122.0387466),
        CLLocationCoordinate2D(latitude: 37.3425773, longitude: -122.0247380),
        CLLocationCoordinate2D(latitude: 37.3426291, longitude: -122.0253387),
        CLLocationCoordinate2D(latitude: 37.3416711, longitude: -122.0256557),
        CLLocationCoordinate2D(latitude: 37.3275180, longitude: -122.0507106),
        CLLocationCoordinate2D(latitude: 37.3187770, longitude: -122.0688523),
        CLLocationCoordinate2D(latitude: 37.3412129, longitude: -121.9753722),
        CLLocationCoordinate2D(latitude: 37.3408061, longitude: -121.9757454),
        CLLocationCoordinate2D(latitude: 37.3597423, longitude: -121.9893887),
        CLLocationCoordinate2D(latitude: 37.3242567, longitude: -122.0117839)
    ]

    var mapView: MKMapView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mapView = MKMapView(frame: self.view.frame)
        view.addSubview(mapView)

        mapView.register(WaterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
        mapView.register(WaterAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)

        addAnnotations()

        let region = MKCoordinateRegion(center: coordinates[0], span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
        mapView.setRegion(region, animated: true)
    }
    
    private func addAnnotations() {
        for coordinate in coordinates {
            let annotation = MKPointAnnotation()
            annotation.coordinate = coordinate
            mapView.addAnnotation(annotation)
        }
    }
}

WaterAnnotationView.swift

class WaterAnnotationView: MKMarkerAnnotationView {
    
    override var annotation: MKAnnotation? {
        didSet { configure(for: annotation) }
    }
    
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        glyphImage = UIImage(systemName: "drop.fill")
        markerTintColor = #colorLiteral(red: 0.005868499167, green: 0.5166643262, blue: 0.9889912009, alpha: 1)
        configure(for: annotation)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configure(for annotation: MKAnnotation?) {
        displayPriority = .required
        clusteringIdentifier = MKMapViewDefaultClusterAnnotationViewReuseIdentifier
    }
}

问题原因

  1. 重用标识符混用:你把自定义的WaterAnnotationView同时注册给了系统默认的普通标注和聚类标注重用标识符,但聚类标注需要的是MKClusterAnnotationView子类,而非MKMarkerAnnotationView。地图进行聚类时,会错误复用普通标注视图,导致样式混乱。
  2. 聚类配置错误:在WaterAnnotationView中设置clusteringIdentifier = MKMapViewDefaultClusterAnnotationViewReuseIdentifier,会让普通标注被当作聚类标注处理,进一步干扰视图重用池的逻辑。

修复方案

1. 修改ViewController代码

为普通标注和聚类标注设置独立的重用标识符,并通过代理方法明确区分视图类型:

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate {

    let coordinates = [
        CLLocationCoordinate2D(latitude: 37.3105042, longitude: -122.0380024),
        CLLocationCoordinate2D(latitude: 37.3099890, longitude: -122.0404633),
        CLLocationCoordinate2D(latitude: 37.3573748, longitude: -122.0692252),
        CLLocationCoordinate2D(latitude: 37.3285758, longitude: -122.0787571),
        CLLocationCoordinate2D(latitude: 37.3311494, longitude: -122.0588439),
        CLLocationCoordinate2D(latitude: 37.3261889, longitude: -122.0626754),
        CLLocationCoordinate2D(latitude: 37.3578807, longitude: -122.0535947),
        CLLocationCoordinate2D(latitude: 37.3497026, longitude: -122.0562993),
        CLLocationCoordinate2D(latitude: 37.3403821, longitude: -121.9736835),
        CLLocationCoordinate2D(latitude: 37.3766856, longitude: -122.0302783),
        CLLocationCoordinate2D(latitude: 37.3656187, longitude: -122.0378590),
        CLLocationCoordinate2D(latitude: 37.3655657, longitude: -122.0374650),
        CLLocationCoordinate2D(latitude: 37.3242644, longitude: -122.0387466),
        CLLocationCoordinate2D(latitude: 37.3425773, longitude: -122.0247380),
        CLLocationCoordinate2D(latitude: 37.3426291, longitude: -122.0253387),
        CLLocationCoordinate2D(latitude: 37.3416711, longitude: -122.0256557),
        CLLocationCoordinate2D(latitude: 37.3275180, longitude: -122.0507106),
        CLLocationCoordinate2D(latitude: 37.3187770, longitude: -122.0688523),
        CLLocationCoordinate2D(latitude: 37.3412129, longitude: -121.9753722),
        CLLocationCoordinate2D(latitude: 37.3408061, longitude: -121.9757454),
        CLLocationCoordinate2D(latitude: 37.3597423, longitude: -121.9893887),
        CLLocationCoordinate2D(latitude: 37.3242567, longitude: -122.0117839)
    ]

    var mapView: MKMapView!
    // 定义独立的重用标识符
    private let waterAnnotationReuseID = "WaterAnnotation"
    private let clusterAnnotationReuseID = "ClusterAnnotation"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mapView = MKMapView(frame: self.view.frame)
        mapView.delegate = self
        view.addSubview(mapView)

        // 注册普通标注视图
        mapView.register(WaterAnnotationView.self, forAnnotationViewWithReuseIdentifier: waterAnnotationReuseID)
        // 注册系统默认聚类标注视图
        mapView.register(MKClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: clusterAnnotationReuseID)

        addAnnotations()

        let region = MKCoordinateRegion(center: coordinates[0], span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
        mapView.setRegion(region, animated: true)
    }
    
    private func addAnnotations() {
        for coordinate in coordinates {
            let annotation = MKPointAnnotation()
            annotation.coordinate = coordinate
            mapView.addAnnotation(annotation)
        }
    }
    
    // 代理方法:为不同类型标注返回对应视图
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKClusterAnnotation {
            let clusterView = mapView.dequeueReusableAnnotationView(withIdentifier: clusterAnnotationReuseID, for: annotation) as! MKClusterAnnotationView
            // 可选:自定义聚类标注样式
            clusterView.markerTintColor = .systemBlue
            return clusterView
        } else {
            let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: waterAnnotationReuseID, for: annotation) as! WaterAnnotationView
            return annotationView
        }
    }
}

2. 修改WaterAnnotationView代码

移除错误的聚类标识符设置,专注于普通标注的样式配置:

class WaterAnnotationView: MKMarkerAnnotationView {
    
    override var annotation: MKAnnotation? {
        didSet { configure(for: annotation) }
    }
    
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        glyphImage = UIImage(systemName: "drop.fill")
        markerTintColor = #colorLiteral(red: 0.005868499167, green: 0.5166643262, blue: 0.9889912009, alpha: 1)
        configure(for: annotation)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configure(for annotation: MKAnnotation?) {
        displayPriority = .required
        // 移除错误的聚类标识符设置
    }
}

修复原理

  • 独立的重用标识符避免了普通标注与聚类标注的视图复用冲突
  • 通过代理方法明确区分标注类型,确保地图正确创建和复用对应视图
  • 移除标注视图中错误的聚类配置,让地图统一管理聚类逻辑

内容的提问来源于stack exchange,提问作者P. Stern

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 11:38:10