Xcode MapKit项目缩放/旋转/移动地图时标注图片错乱问题
问题原因
地图缩放、移动、旋转后标注图片串位但文本正确,是MKAnnotationView复用机制下自定义控件未同步更新数据导致的。
现有代码在取出复用队列中的标注视图时,仅更新了视图的annotation属性:MKMarkerAnnotationView内置的标题、副标题控件会自动根据新绑定的annotation更新内容,因此文本始终匹配;但自定义添加的rightCalloutAccessoryView按钮属于手动添加的外部控件,不会自动随annotation属性更新,复用后按钮上保留的是上一次绑定标注的旧图片,最终出现图片混淆。
不建议采用地图移动停止后重载所有标注的方案,这种方式会强制销毁重建所有标注视图,带来不必要的性能开销,还容易引发标注闪烁、弹出的callout意外关闭等问题。
修复方案
核心原则是:所有和标注数据绑定的UI配置逻辑,不要只写在新创建视图的else分支里,要放在复用判断逻辑之外,保证新创建和复用的视图都会被正确配置数据。
修复后的代理方法实现如下:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard let annotation = annotation as? buildings else { return nil } let identifier = "buildings" var view: MKMarkerAnnotationView if let dequeuedView = mapView.dequeueReusableAnnotationView( withIdentifier: identifier) as? MKMarkerAnnotationView { // 复用视图仅更新annotation绑定 dequeuedView.annotation = annotation view = dequeuedView } else { // 分支内仅做视图的初始化创建,不写任何和业务数据绑定的逻辑 view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier) view.canShowCallout = true view.calloutOffset = CGPoint(x: -5, y: 5) let coolButton = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) view.rightCalloutAccessoryView = coolButton } // 统一做数据绑定:新创建、复用的视图都会走到这一步,更新按钮图片 if let calloutButton = view.rightCalloutAccessoryView as? UIButton { calloutButton.setImage(annotation.buildingImage, for: .normal) } return view }
优化建议
- 遵循Swift命名规范,将标注模型类名从全小写开头的
buildings改为大驼峰格式Building,避免和实例变量产生语义混淆 - 如果后续需要给标注本身设置自定义图标(通过
glyphImage、markerTintColor等属性)、给callout添加其他自定义子视图,都要把对应的数据赋值逻辑放在上述统一配置的位置,不要仅在视图创建分支中设置,否则依然会出现复用串内容的问题 - 不需要额外监听地图移动、缩放、旋转的结束事件做标注重载,原生复用机制在正确配置的情况下完全可以保证内容匹配,且性能最优
内容的提问来源于stack exchange,提问作者Drew
相关产品推荐
相关产品推荐

