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 } }
问题原因
- 重用标识符混用:你把自定义的
WaterAnnotationView同时注册给了系统默认的普通标注和聚类标注重用标识符,但聚类标注需要的是MKClusterAnnotationView子类,而非MKMarkerAnnotationView。地图进行聚类时,会错误复用普通标注视图,导致样式混乱。 - 聚类配置错误:在
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
相关产品推荐
相关产品推荐

