0

I am trying to display any overlay on the map. I have followed many many tutorials but nothing works. The map will always load with no errors but the overlay will never show on the map.

I am trying to get this , an inverted circle, to show on the map.

import Foundation
import UIKit
import MapKit

class MKInvertedCircleOverlayRenderer: MKOverlayRenderer {

var fillColor: UIColor = UIColor.red
var strokeColor: UIColor = UIColor.blue
var lineWidth: CGFloat = 3
var circle: MKCircle

init(circle: MKCircle) {
    self.circle = circle
    super.init(overlay: circle)
}

override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
    let path = UIBezierPath(rect: rect(for: MKMapRect.world))

    let excludePath: UIBezierPath = 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(9 / zoomScale)
    context.setStrokeColor(strokeColor.cgColor)
    context.strokePath()

    //line showing circle radius
    let lineBeginPoint = CGPoint(x: excludePath.bounds.midX, y: excludePath.bounds.midY)
    let lineEndPoint = CGPoint(x: excludePath.bounds.maxX, y: excludePath.bounds.midY)
    let linePath: UIBezierPath = UIBezierPath()
    linePath.move(to: lineBeginPoint)
    linePath.addLine(to: lineEndPoint)

    context.addPath(linePath.cgPath)
    context.setLineWidth(6/zoomScale)
    context.setStrokeColor(UIColor.black.cgColor)
    context.setLineDash(phase: 1, lengths: [20 / zoomScale, 10 / zoomScale])
    context.strokePath()

    // circle at the end of the line above
    let circleSize: CGFloat = 30/zoomScale
    let circleRect = CGRect(origin: CGPoint(x: lineEndPoint.x - (circleSize/2), y: lineEndPoint.y - (circleSize/2)),
                            size: CGSize(width: circleSize, height: circleSize))

    let circlePath: UIBezierPath =
        UIBezierPath(roundedRect: circleRect, cornerRadius: circleSize)

    context.addPath(circlePath.cgPath)
    context.setFillColor(UIColor.black.cgColor)
    context.fillPath()
}
}
import Foundation
import MapKit
import UIKit

class MKInvertedCircle : NSObject, MKOverlay {

    var coordinate: CLLocationCoordinate2D

    var boundingMapRect: MKMapRect {
        return MKMapRect.world
    }

    init(center coord: CLLocationCoordinate2D) {
        self.coordinate = coord
    }
}

viewcontroller

import MapKit
import UIKit
import Foundation





class ViewController: UIViewController, MKMapViewDelegate {

    private let locationManager = CLLocationManager()
    private var currentCoordinate: CLLocationCoordinate2D?



    @IBOutlet weak var mapView: MKMapView!


  func addCircleOverlay() {
    let overlay = MKInvertedCircle(center: CLLocationCoordinate2D())
    mapView.addOverlay(overlay)
  }

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKInvertedCircle {
        let circleRenderer = MKInvertedCircleOverlayRenderer(circle: MKCircle())

        return circleRenderer
    }
    else {
       return MKOverlayRenderer(overlay: overlay)
    }
    }


        // if screen loads ask for location permissions
    override func viewDidLoad() {
        super.viewDidLoad()
        configureLocationServices()
        mapView.delegate = self;

        // Do any additional setup after loading the view.
    }


    //how to ask for location permissions
    private func configureLocationServices() {

        locationManager.delegate = self
        //check location permissions
        let status = CLLocationManager.authorizationStatus()
        //if location permissions not set, ask
        if status == .notDetermined {
            locationManager.requestAlwaysAuthorization()
        } else if status == .authorizedAlways || status == .authorizedWhenInUse {
            //begin tracking location
            beginLocationUpdates(locationManager: locationManager)

        }
    }
    //begin tracking location
    private func beginLocationUpdates(locationManager: CLLocationManager) {
        //show blue dot
        mapView.showsUserLocation = true
        //track location to best of phones ability
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        //start updating location
        locationManager.startUpdatingLocation()

    }
    //zoom to location
    private func zoomToLatestLocation(with coordinate: CLLocationCoordinate2D) {
        //set zoom level
        let zoomRegion = MKCoordinateRegion.init(center: coordinate, latitudinalMeters: 10000, longitudinalMeters: 10000)
        //tells map to zoom animated
        mapView.setRegion(zoomRegion, animated: true)

    }

}

extension ViewController: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("Did get Latest Location")

        guard let latestLocation = locations.first else {return }

        if currentCoordinate == nil{
            zoomToLatestLocation(with: latestLocation.coordinate)
        }

        currentCoordinate = latestLocation.coordinate

    }
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            print("The Status Changed")
        if status == .authorizedAlways || status == .authorizedWhenInUse {
            beginLocationUpdates(locationManager:  manager)
        }
    }




}

But I will settle for any overlay working at all as a step in the right direction. Does anyone know of up to date code that will display an overlay on the map? Thanks.

  • Welcome to SO! Please share some code that you have tried and explain what exactly goes wrong. "Nothing works" is not very indicative. It's difficult to help with out any more information. See also [ask]. – koen Dec 30 '19 at 20:52
  • Hey @koen, sorry for not being specific. I have updated the post with all the code I have. I have followed other stack overflow questions to try and trouble shoot this problem such as [this one](https://stackoverflow.com/questions/38066832/how-do-i-overlay-a-circle-at-a-set-location-using-mapkit-and-swift) and [this blog](https://www.raywenderlich.com/425-mapkit-tutorial-overlay-views#toc-anchor-007) however even after copy and pasting the code from these nothing shows up on the map. I am new to swift so I don't really understand the code I am using. – Jesse Hayes Dec 30 '19 at 21:35

1 Answers1

0

the error seems to be your delegate's

mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer

instead of using the overlay in this function, you're initialising a new MKCircle.

Try this instead

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if let circle = overlay as? MKCircle {
        return MKInvertedCircleOverlayRenderer(circle: circle)
    } else {
        return MKOverlayRenderer(overlay: overlay)
    }
}