I have two MKCircle overlays rendered with a MKCircleRenderer and another custom one. At the bottom of the rendered circles, I want to show a custom view (containing a label and text) which are independent from the zoom level, in other words, they have to be always visible in the map. For this reason, I've created a custom MKAnnnotationView which works great for this.
I'd like to move the annotation to the bottom-center of the circle overlay, so that overlay and annotation look like a single element on the map.
This is the final result I want to obtain
Annotation View Class:
class MyAnnotationView: MKAnnotationView {
static var identifier = "MyAnnotationView"
init(
annotation: GlueAnnotation?,
reuseIdentifier: String?
) {
super.init(
annotation: annotation,
reuseIdentifier: reuseIdentifier
)
guard let annotation = annotation else { return }
let annotationFrame = CGRect(x: 0, y: 0, width: 125, height: 30)
self.frame = annotationFrame
self.backgroundColor = .clear
let image = UIImageView(frame: annotationFrame.offsetBy(dx: 6, dy: 0)).with {
$0.image = annotation.style.image.uiKit
$0.contentMode = .left
}
self.addSubview(image)
guard let title = annotation.title else { return }
let label = UILabel(frame: annotationFrame.offsetBy(dx: 32, dy: 4)).with {
$0.font = Font(style: .subheadline, emphasis: .bold).uiKit
$0.textColor = BackgroundColor.primary.uiKit
$0.textAlignment = .center
$0.text = title
$0.sizeToFit()
}
self.addSubview(label)
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) not implemented!")
}
override func draw(_ rect: CGRect) {
guard let annotation = annotation as? GlueAnnotation else { return }
guard let context = UIGraphicsGetCurrentContext() else { return }
context.beginPath()
context.move(to: CGPoint(x: rect.minX, y: rect.minY))
context.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
context.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
context.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
context.closePath()
let color = annotation.style.color.uiKit
color.set()
context.fillPath()
}
}
Overlay View Class
public class InvertedCircleOverlayRenderer: MKOverlayRenderer {
public var fillColor = UIColor.red
public var strokeColor = UIColor.blue
public var lineWidth: CGFloat = 3
public var circle: MKCircle
public init(circle: MKCircle) {
self.circle = circle
super.init(overlay: circle)
}
public override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
let path = UIBezierPath(rect: rect(for: MKMapRect.world))
let excludePath = UIBezierPath(roundedRect: CGRect(x: circle.coordinate.latitude,
y: circle.coordinate.longitude,
width: circle.boundingMapRect.size.width,
height: circle.boundingMapRect.size.height),
cornerRadius: CGFloat(circle.boundingMapRect.size.width))
context.setFillColor(fillColor.cgColor)
path.append(excludePath)
context.addPath(path.cgPath)
context.fillPath(using: .evenOdd)
context.addPath(excludePath.cgPath)
context.setLineWidth(lineWidth / zoomScale)
context.setStrokeColor(strokeColor.cgColor)
context.strokePath()
}
}