0

I have a MapView and the user can select a radius to show an area. I add a MKCircle as an overlay. When the radius changes from 30 miles to 1 mile there is a noticeable glitch on the perimeter of the MKCircle while the MapView zooms in. The glitch looks sort of like a flair. It only happens when zooming in and not zooming out.\

Since the zoom constantly changes I remove the old overlay before adding another one but I don't think that's the issue.

How can I remove the glitch on the circle as the MapView's zoom changes?

@IBAction func newRadiusButtonTapped(sender: UIButton) {

    // coordinate is the users location and span was 10 miles now it's 1 mile
    let region = MKCoordinateRegionMake(location.coordinate, span)
    mapView.setRegion(region, animated: true)

    let circle = MKCircle(center: location.coordinate, radius: radius)

     // remove old overlay before adding another one
     for overlay in mapView.overlays {
         mapView.remove(overlay)
     }

     view.layoutIfNeeded()
     // mapView.layoutIfNeeded() I tried this but it didn't make a difference
     mapView.add(circle)
}

30 miles

glitch

1 mile

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • Just a question: You remove ALL the overlays, is that necessary? if you store the circle before, you just need to remove this special one. By removing ALL there may be some side effects... And the classic question: do you do it on the main thread? – Hardy_Germany Nov 03 '18 at 19:17
  • The reason why I kept removing the overlays was because when I didn't remove them ut kept adding overlays on top of overlays every time the button would get pressed (mapView.add(circle) was adding them). I tried 'mapView.remove(overlay)' without the for loop and it had no effect. I didn't trying storing the circle as a class property yet. I thought hiding and unhiding it might make it look odd. Is that what your suggesting? – Lance Samaria Nov 03 '18 at 19:22
  • yes, yes, but as far as I remember, you can store the generated circle in a class property. And with this reference, you can just remove it: So have a class property "var Circle: MKCircle?", and inside your method: "if circle != nil { mapView.remove(Circle)}", and: instead of "let circle = MKCircle(..." use "Circle = MKCircle( ...". This should work.. – Hardy_Germany Nov 03 '18 at 19:28
  • @Hardy_Germany I added it as a class property, initialized it when the button was pressed, and it made no difference. Same glitch. Thanks anyway :) – Lance Samaria Nov 03 '18 at 19:36
  • mmhm strange.. did you try "func exchangeOverlay(MKOverlay, with: MKOverlay) Exchanges the positions of the two overlay objects." with this you can exchange the old circle by the new circle... I use such overlays a lot and I never had such glitches .. so there must be a reason for it. – Hardy_Germany Nov 03 '18 at 19:46
  • just for a try can you encapsulate the remove and add calls inside a "dispatchQueue.main.async(execute: { < place here the call to remove or add the overlay> })" – Hardy_Germany Nov 03 '18 at 19:49
  • nothing, I just tried the exchange func(), then I wrapped in a dispatchQueue, then I tried it the original way I had it and removed the overlays in the dispatchQueue (that completely removed it and didn't add it back?), then i added the overlay in the queue, then I called view.layoutIfNeeded in the queue, nothing. I just tried 7 things. Strange issue. – Lance Samaria Nov 03 '18 at 20:02
  • Well I'm sorry, than someone else have to step in .. I reached the end of my wisdom ;-) – Hardy_Germany Nov 03 '18 at 20:11
  • Lmao, thanks for the advice. You gave me a new method (exachange) so I did learn something new. Much appreciated!!! Enjoy your weekend – Lance Samaria Nov 03 '18 at 20:12
  • @Hardy_Germany hi there again, I couldn't find the reason for the glitch but I found a great work around. I think the zoom was causing the glitch. Maybe both animations happening at once without any type of completion handler or notifier? Who knows... Thanks for the help! – Lance Samaria Nov 03 '18 at 23:40

1 Answers1

0

I couldn't find what was causing the glitch but I found this delegate method from this answer on the mapView that gets notified after the mapView region finishes changing. I add the overlay there

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { 
}

Simple process:

I create a circle property of type MKCircle? I also create property named shouldAddCircle of type Bool and set it to true. When the button is pressed I initial the circle property with the MKCircle that I create inside the button and set the shouldAddCircle to true. Inside the button function I remove all the mapViews overlays.

Inside the delegate method I now check to see that the shouldAddCircle property is true and if it is I then check to see make sure the circle property isn't nil. If they match then I add the initialized circle to the mapView. After I add the circle to the mapView I have to set the shouldAddCircle to false because every time the user scrolls the map regionDidChangeAnimated gets called and it will keep adding overlays to the map.

Here' the code below. Be sure to add mapView.delegate = self in viewDidLoad and to set the MKMapViewDelegate before everything.

var circle: MKCircle?
var shouldAddCircle = true

@IBAction func newRadiusButtonTapped(sender: UIButton) {

    // coordinate is the users location and span was 10 miles now it's 1 mile
    let region = MKCoordinateRegionMake(location.coordinate, span)
    mapView.setRegion(region, animated: true)

    let circle = MKCircle(center: location.coordinate, radius: radius)

    // set the circle property to match the circle that was just created
    self.circle = circle

    // set this true
    shouldAddCircle = true

    // remove old overlay before adding another one
    for overlay in mapView.overlays {
        mapView.remove(overlay)
    }
}

// this function gets called repeatedly as the mapView is zoomed and/or panned
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {

    // make sure this is true because that means the user updated the radius
    if shouldAddCircle {

       // make sure the circle isn't ni
       if let circle = self.circle {
           // after the mapView finishes add the circle to it
           mapView.add(circle)

           // set this to false so that this doesn't called again until the user presses the button where they set it to true
           shouldAddCircle = false
       }
    }
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • 1
    Well done! I just would recommend to remove just the circleOverlay and not all overlays. Simply to avoid any side effect. You can do this by add "if self.circle != nil {mapView.remove(self.circle)} before the "let circle = MKCircle ..." statement and remove the for loop to remove all overlays. – Hardy_Germany Nov 04 '18 at 12:14
  • @Hardy_Germany good idea, thanks, that’s makes a lot of sense. Much appreciated – Lance Samaria Nov 04 '18 at 14:38