How to create MKCircle in Swift?
Asked Answered
P

3

9

Iv been searching all around for a good explanation of how to make an MKCircle annotation for the MapView using Swift 2.0 but I cant seem to find an adequate explanation. Can someone post some example code showing how to create the MKCircle annotation? Here is the code i'm using to make the map and get the coordinate.

let address = self.location

let geocoder = CLGeocoder()

    geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
        if((error) != nil){
            print("Error", error)
        }
        if let placemark = placemarks?.first {
            let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate

            self.locationCoordinates = coordinates
            let span = MKCoordinateSpanMake(0.005, 0.005)
            let region = MKCoordinateRegion(center: self.locationCoordinates, span: span)
            self.CIMap.setRegion(region, animated: true)

            let annotation = MKPointAnnotation()
            annotation.coordinate = self.locationCoordinates
            self.CIMap.addAnnotation(annotation)

            self.CIMap.layer.cornerRadius = 10.0

            self.CIMap.addOverlay(MKCircle(centerCoordinate: self.locationCoordinates, radius: 1000))
        }
    })
Paleolithic answered 23/10, 2015 at 0:38 Comment(0)
A
6

Will show step wise approach about how to create a circular overlay on map view using swift 3 with xcode 8.3.3

In your main storyboard file drag map kit view on to the scene(view) of storyboard and create outlet for the same, here i created mapView. Also you want to create overlay dynamically whenever you go for long press on map, so drag Long Press Gesture Recognizer on to the mapView from object libary and then create action method for the same, here i had created addRegion() for the same.

  1. create a global constant for CLLocationManager class so that it can be accessed in every function. And in your viewDidLoad method, add some code for getting authorization from the user.

        import UIKit  
        import MapKit
    
        class ViewController: UIViewController {
    
    
            @IBOutlet var mapView: MKMapView!
            let locationManager = CLLocationManager()
    
        override func viewDidLoad() {  
                super.viewDidLoad()
    
                locationManager.delegate = self
                locationManager.requestAlwaysAuthorization()
                locationManager.requestWhenInUseAuthorization()
                locationManager.desiredAccuracy = kCLLocationAccuracyBest
                locationManager.startUpdatingLocation()
    
            }
    
  2. Add code for generating a circular region whenever you do long press gesture recognizer in your long press gesture recognizer action method addRegion().

        @IBAction func addRegion(_ sender: Any) {  
                print("addregion pressed")  
                guard let longPress = sender as? UILongPressGestureRecognizer else {return}
    
                let touchLocation = longPress.location(in: mapView)
                let coordinates = mapView.convert(touchLocation, toCoordinateFrom: mapView)
                let region = CLCircularRegion(center: coordinates, radius: 5000, identifier: "geofence")
                mapView.removeOverlays(mapView.overlays)
                locationManager.startMonitoring(for: region)
                let circle = MKCircle(center: coordinates, radius: region.radius)
                mapView.add(circle)
    
            }
    

Still you won't see circle physically on the map until you render circle on map. for this you need to implement delegate of mapviewdelegate.

        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {}
  1. To make code look more cleaner you can create extension after last brace where your class ends. One extension contains code for CLLocationManagerDelegate and other for MKMapViewDelegate.

        extension ViewController: CLLocationManagerDelegate {
            func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
                locationManager.stopUpdatingLocation()
                mapView.showsUserLocation = true
            }
        }
    

you should call locationManager.stopUpdatingLocation() in delegate method so that your battery does not drain out.

        extension ViewController: MKMapViewDelegate {
            func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
                guard let circelOverLay = overlay as? MKCircle else {return MKOverlayRenderer()}

                let circleRenderer = MKCircleRenderer(circle: circelOverLay)
                circleRenderer.strokeColor = .blue
                circleRenderer.fillColor = .blue
                circleRenderer.alpha = 0.2
                return circleRenderer
            }
        }

Here we are making the actual circle on the screen.

Final code should look like this.

import UIKit
import MapKit

class ViewController: UIViewController {


    @IBOutlet var mapView: MKMapView!
    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()
        locationManager.requestWhenInUseAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()

    }

    // MARK: Long Press Gesture Recognizer Action Method

    @IBAction func addRegion(_ sender: Any) {
        print("addregion pressed")
        guard let longPress = sender as? UILongPressGestureRecognizer else {return}

        let touchLocation = longPress.location(in: mapView)
        let coordinates = mapView.convert(touchLocation, toCoordinateFrom: mapView)
        let region = CLCircularRegion(center: coordinates, radius: 5000, identifier: "geofence")
        mapView.removeOverlays(mapView.overlays)
        locationManager.startMonitoring(for: region)
        let circle = MKCircle(center: coordinates, radius: region.radius)
        mapView.add(circle)

    }

}

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        locationManager.stopUpdatingLocation()
        mapView.showsUserLocation = true
    }
}

extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        guard let circelOverLay = overlay as? MKCircle else {return MKOverlayRenderer()}

        let circleRenderer = MKCircleRenderer(circle: circelOverLay)
        circleRenderer.strokeColor = .blue
        circleRenderer.fillColor = .blue
        circleRenderer.alpha = 0.2
        return circleRenderer
    }
}
Amylose answered 13/7, 2017 at 10:38 Comment(0)
G
16

First you need to add MKMapViewDelegate to the class defenition.

 mapView.delegate = self 

Set the maps delegate to self in your viewDidLoad.

Setting the annotation

mapView.addOverlay(MKCircle(centerCoordinate: CLLocationCoordinate2D, radius: CLLocationDistance))

mapView rendererForOverlay should now be called in the mapViews delegate and there you get to draw it

func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
    if let overlay = overlay as? MKCircle {
        let circleRenderer = MKCircleRenderer(circle: overlay)
        circleRenderer.fillColor = UIColor.blueColor()
        return circleRenderer
    }
}

Also, you need to import MapKit for it all to compile

Gony answered 23/10, 2015 at 0:57 Comment(4)
Thank you for the help Animal, I was able to get it working.Paleolithic
Great answer! BTW nowadays you can't have a conditional return (only) in a function that must return a value. And rendererForOverlay forces you to return a non-null value. So I don't know what I could do if I ever got an unknown overlay. I guess that would be a crash.Thermoelectrometer
default > return MKOverlayRenderer(overlay: overlay) in the end.Staggs
@Gony hey, I got a problem with this solution, everytime the user locations moves, the overlay overlaps each other, i just want to show one at a time. can you help me?Interceptor
A
6

Will show step wise approach about how to create a circular overlay on map view using swift 3 with xcode 8.3.3

In your main storyboard file drag map kit view on to the scene(view) of storyboard and create outlet for the same, here i created mapView. Also you want to create overlay dynamically whenever you go for long press on map, so drag Long Press Gesture Recognizer on to the mapView from object libary and then create action method for the same, here i had created addRegion() for the same.

  1. create a global constant for CLLocationManager class so that it can be accessed in every function. And in your viewDidLoad method, add some code for getting authorization from the user.

        import UIKit  
        import MapKit
    
        class ViewController: UIViewController {
    
    
            @IBOutlet var mapView: MKMapView!
            let locationManager = CLLocationManager()
    
        override func viewDidLoad() {  
                super.viewDidLoad()
    
                locationManager.delegate = self
                locationManager.requestAlwaysAuthorization()
                locationManager.requestWhenInUseAuthorization()
                locationManager.desiredAccuracy = kCLLocationAccuracyBest
                locationManager.startUpdatingLocation()
    
            }
    
  2. Add code for generating a circular region whenever you do long press gesture recognizer in your long press gesture recognizer action method addRegion().

        @IBAction func addRegion(_ sender: Any) {  
                print("addregion pressed")  
                guard let longPress = sender as? UILongPressGestureRecognizer else {return}
    
                let touchLocation = longPress.location(in: mapView)
                let coordinates = mapView.convert(touchLocation, toCoordinateFrom: mapView)
                let region = CLCircularRegion(center: coordinates, radius: 5000, identifier: "geofence")
                mapView.removeOverlays(mapView.overlays)
                locationManager.startMonitoring(for: region)
                let circle = MKCircle(center: coordinates, radius: region.radius)
                mapView.add(circle)
    
            }
    

Still you won't see circle physically on the map until you render circle on map. for this you need to implement delegate of mapviewdelegate.

        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {}
  1. To make code look more cleaner you can create extension after last brace where your class ends. One extension contains code for CLLocationManagerDelegate and other for MKMapViewDelegate.

        extension ViewController: CLLocationManagerDelegate {
            func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
                locationManager.stopUpdatingLocation()
                mapView.showsUserLocation = true
            }
        }
    

you should call locationManager.stopUpdatingLocation() in delegate method so that your battery does not drain out.

        extension ViewController: MKMapViewDelegate {
            func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
                guard let circelOverLay = overlay as? MKCircle else {return MKOverlayRenderer()}

                let circleRenderer = MKCircleRenderer(circle: circelOverLay)
                circleRenderer.strokeColor = .blue
                circleRenderer.fillColor = .blue
                circleRenderer.alpha = 0.2
                return circleRenderer
            }
        }

Here we are making the actual circle on the screen.

Final code should look like this.

import UIKit
import MapKit

class ViewController: UIViewController {


    @IBOutlet var mapView: MKMapView!
    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()
        locationManager.requestWhenInUseAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()

    }

    // MARK: Long Press Gesture Recognizer Action Method

    @IBAction func addRegion(_ sender: Any) {
        print("addregion pressed")
        guard let longPress = sender as? UILongPressGestureRecognizer else {return}

        let touchLocation = longPress.location(in: mapView)
        let coordinates = mapView.convert(touchLocation, toCoordinateFrom: mapView)
        let region = CLCircularRegion(center: coordinates, radius: 5000, identifier: "geofence")
        mapView.removeOverlays(mapView.overlays)
        locationManager.startMonitoring(for: region)
        let circle = MKCircle(center: coordinates, radius: region.radius)
        mapView.add(circle)

    }

}

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        locationManager.stopUpdatingLocation()
        mapView.showsUserLocation = true
    }
}

extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        guard let circelOverLay = overlay as? MKCircle else {return MKOverlayRenderer()}

        let circleRenderer = MKCircleRenderer(circle: circelOverLay)
        circleRenderer.strokeColor = .blue
        circleRenderer.fillColor = .blue
        circleRenderer.alpha = 0.2
        return circleRenderer
    }
}
Amylose answered 13/7, 2017 at 10:38 Comment(0)
T
1

An overlay is merely a set of numbers. The thing that is visible in the map view is an overlay renderer. You must implement mapView:rendererForOverlay: to supply the overlay renderer; otherwise, you will see nothing.

Tidings answered 23/10, 2015 at 0:48 Comment(1)
what a descriptive answer so helpfulTelephonist

© 2022 - 2024 — McMap. All rights reserved.