如何在MKSnapshot中绘制与实时MapView一致的带标题标注?
嘿,我来帮你搞定这个MKSnapshot的标注问题!你遇到的旧版大头针样式、标注标题不显示的问题,其实都是因为沿用了iOS7时代的老代码,没适配iOS11之后推出的新标注API。下面一步步来解决:
解决MKSnapshot标注样式与标题显示问题
1. 改用iOS11+的MKMarkerAnnotationView实现点状样式
iOS11之后苹果引入了MKMarkerAnnotationView,它就是你在实时MapView里看到的那种扁平化点状标注,完全替代了旧版的MKPinAnnotationView。你需要把创建标注视图的代码切换到这个类:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard !(annotation is MKUserLocation) else { return nil } let reuseId = "MarkerAnnotation" var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKMarkerAnnotationView if annotationView == nil { annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseId) // 配置和实时MapView一致的样式 annotationView?.markerTintColor = .systemBlue // 自定义标注颜色,按需调整 annotationView?.glyphImage = nil // 设为nil就是纯点状样式,不需要额外图标 annotationView?.canShowCallout = true // 开启标题/副标题气泡显示 } else { annotationView?.annotation = annotation } return annotationView }
2. 让快照显示标注标题
默认情况下,MKSnapshot不会自动渲染标注的callout(标题气泡),这里有两种靠谱的解决方式:
- 方式一:利用
MKMarkerAnnotationView的原生特性,只要你给MKAnnotation对象设置了title属性,并且开启了canShowCallout,在快照绘制标注视图时就能带出标题气泡。 - 方式二:如果需要更精准的标题位置控制,可以在快照生成后,用Core Graphics手动把标题绘制到图片上:
// 快照完成后手动绘制标题的示例代码 snapshotter.start { snapshot, error in guard let snapshot = snapshot else { return } var finalImage = snapshot.image UIGraphicsBeginImageContextWithOptions(finalImage.size, false, finalImage.scale) finalImage.draw(at: .zero) // 遍历所有标注,计算标题位置并绘制 for annotation in yourAnnotationArray { let coordPoint = snapshot.point(for: annotation.coordinate) // 调整偏移量让标题显示在标注上方合适位置 let titleRect = CGRect(x: coordPoint.x + 12, y: coordPoint.y - 35, width: 160, height: 30) let titleAttrs: [NSAttributedString.Key: Any] = [ .font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.black ] annotation.title?.draw(in: titleRect, withAttributes: titleAttrs) } finalImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() // 把最终图片赋值给UITableViewCell里的UIImageView yourCellMapImageView.image = finalImage }
3. 确保快照配置与实时MapView完全一致
别忘了把MKMapSnapshotter.Options的各项属性和你的实时MapView同步,这样生成的快照视觉效果才会完全匹配:
region: 和MapView的region保持一致scale: 设置为UIScreen.main.scale保证高清度showsBuildings/showsPointsOfInterest: 和MapView的对应开关保持相同状态
完整快照生成示例代码
这里给你整合了所有要点的完整代码,直接调用就能生成和实时MapView一致的标注快照:
func generateMatchingMapSnapshot(with annotations: [MKAnnotation], region: MKCoordinateRegion, cellImageSize: CGSize, completion: @escaping (UIImage?) -> Void) { let options = MKMapSnapshotter.Options() options.region = region options.scale = UIScreen.main.scale options.size = cellImageSize options.showsBuildings = true options.showsPointsOfInterest = true let snapshotter = MKMapSnapshotter(options: options) snapshotter.start { snapshot, error in guard let snapshot = snapshot else { completion(nil) return } // 创建临时MapView来生成标注视图(快照本身不会自动创建标注) let tempMapView = MKMapView(frame: CGRect(origin: .zero, size: snapshot.image.size)) tempMapView.region = region tempMapView.addAnnotations(annotations) tempMapView.layoutIfNeeded() // 触发标注视图的布局 // 将快照和标注视图合并成最终图片 UIGraphicsBeginImageContextWithOptions(snapshot.image.size, false, snapshot.image.scale) snapshot.image.draw(at: .zero) for annotation in annotations { if let annotationView = tempMapView.view(for: annotation) { // 计算标注视图在快照上的正确位置 let coordPoint = snapshot.point(for: annotation.coordinate) annotationView.frame.origin = CGPoint( x: coordPoint.x - annotationView.bounds.width/2, y: coordPoint.y - annotationView.bounds.height ) annotationView.drawHierarchy(in: annotationView.frame, afterScreenUpdates: true) } } let finalImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() completion(finalImage) } }
内容的提问来源于stack exchange,提问作者Joost van den Akker
相关产品推荐
相关产品推荐

