iOS地图中为MKCircle与MKPolygon实现条件化着色方案
解决MKCircle/MKPolygon无法子类化,实现不同颜色绘制的方案
嘿,这个问题我之前也碰到过!苹果确实把MKCircle和MKPolygon设计成了不可子类化的类,直接给它们加自定义属性的路子走不通,但我们有两个很实用的方案可以绕开这个限制,完美实现每个图形用不同颜色渲染的需求。
方案一:利用Overlay的userInfo属性存储自定义数据
所有MKOverlay的子类都自带一个userInfo字典属性,我们可以把你的Region实例(或者直接把颜色信息)存在这里,在渲染的时候取出来用就行,这是最省事的方法。
步骤1:创建Overlay时绑定自定义数据
// 示例:创建MKCircle并绑定你的Region实例 let circleCenter = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194) let circle = MKCircle(center: circleCenter, radius: 1000) // 把你的Region实例存到userInfo里 circle.userInfo = ["customRegion": yourRegionInstance] // 同理处理MKPolygon let polygonCoordinates = [ CLLocationCoordinate2D(latitude: 37.78, longitude: -122.42), CLLocationCoordinate2D(latitude: 37.79, longitude: -122.43), CLLocationCoordinate2D(latitude: 37.77, longitude: -122.43) ] let polygon = MKPolygon(coordinates: polygonCoordinates, count: polygonCoordinates.count) polygon.userInfo = ["customRegion": anotherRegionInstance] // 添加到地图 mapView.addOverlay(circle) mapView.addOverlay(polygon)
步骤2:在渲染方法中读取数据并设置颜色
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { var renderer: MKOverlayRenderer if let circle = overlay as? MKCircle { let circleRenderer = MKCircleRenderer(circle: circle) // 从userInfo中取出Region,设置对应颜色 if let region = circle.userInfo?["customRegion"] as? Region { circleRenderer.fillColor = region.fillColor.withAlphaComponent(0.3) circleRenderer.strokeColor = region.strokeColor circleRenderer.lineWidth = region.lineWidth } else { // 设置默认颜色,避免空值崩溃 circleRenderer.fillColor = UIColor.blue.withAlphaComponent(0.3) circleRenderer.strokeColor = UIColor.darkGray } renderer = circleRenderer } else if let polygon = overlay as? MKPolygon { let polygonRenderer = MKPolygonRenderer(polygon: polygon) if let region = polygon.userInfo?["customRegion"] as? Region { polygonRenderer.fillColor = region.fillColor.withAlphaComponent(0.3) polygonRenderer.strokeColor = region.strokeColor polygonRenderer.lineWidth = region.lineWidth } else { polygonRenderer.fillColor = UIColor.green.withAlphaComponent(0.3) polygonRenderer.strokeColor = UIColor.darkGray } renderer = polygonRenderer } else { // 其他类型Overlay的默认渲染器 renderer = MKOverlayRenderer(overlay: overlay) } return renderer }
方案二:自定义包装类实现MKOverlay协议
如果你的业务逻辑比较复杂,想要更清晰的类型安全,可以自己写一个包装类,遵守MKOverlay协议,内部持有MKCircle/MKPolygon和你的Region实例,把Overlay的属性转发给内部的图形对象。
步骤1:创建自定义包装类
class ColoredMapOverlay: NSObject, MKOverlay { // 内部持有的系统Overlay对象 let baseOverlay: MKOverlay // 你的自定义Region实例 let region: Region // 实现MKOverlay协议的属性,转发给baseOverlay var coordinate: CLLocationCoordinate2D { baseOverlay.coordinate } var boundingMapRect: MKMapRect { baseOverlay.boundingMapRect } init(baseOverlay: MKOverlay, region: Region) { self.baseOverlay = baseOverlay self.region = region super.init() } }
步骤2:添加包装后的Overlay到地图
let circle = MKCircle(center: circleCenter, radius: 1000) let coloredCircleOverlay = ColoredMapOverlay(baseOverlay: circle, region: yourRegionInstance) mapView.addOverlay(coloredCircleOverlay) let polygon = MKPolygon(coordinates: polygonCoordinates, count: polygonCoordinates.count) let coloredPolygonOverlay = ColoredMapOverlay(baseOverlay: polygon, region: anotherRegionInstance) mapView.addOverlay(coloredPolygonOverlay)
步骤3:在渲染方法中处理自定义Overlay
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { // 先判断是不是我们的自定义包装类 guard let coloredOverlay = overlay as? ColoredMapOverlay else { // 其他Overlay用默认渲染器 return MKOverlayRenderer(overlay: overlay) } let region = coloredOverlay.region let baseOverlay = coloredOverlay.baseOverlay // 根据内部的baseOverlay类型创建对应的渲染器 if let circle = baseOverlay as? MKCircle { let renderer = MKCircleRenderer(circle: circle) renderer.fillColor = region.fillColor.withAlphaComponent(0.3) renderer.strokeColor = region.strokeColor renderer.lineWidth = region.lineWidth return renderer } else if let polygon = baseOverlay as? MKPolygon { let renderer = MKPolygonRenderer(polygon: polygon) renderer.fillColor = region.fillColor.withAlphaComponent(0.3) renderer.strokeColor = region.strokeColor renderer.lineWidth = region.lineWidth return renderer } return MKOverlayRenderer(overlay: overlay) }
方案对比
- 方案一:代码量少,快速实现,适合简单场景;缺点是
userInfo是字典类型,需要强制类型转换,类型安全性稍弱。 - 方案二:类型安全,逻辑清晰,适合复杂业务场景;缺点是需要额外编写包装类,代码量略多。
你可以根据自己的项目需求选择合适的方案,亲测两种方法都能完美解决问题!
内容的提问来源于stack exchange,提问作者lorenzo
相关产品推荐
相关产品推荐

