Search code examples
iosswiftfirebase-realtime-databasemapkitmapkitannotation

Weird custom annotation behaviour when adding/removing annotations


I am in the middle of developing an application for iOS and have come across an issue with custom annotations on my map view.

Here is the idea:

1) I load in the users location to the map, from this location I send a request to my back end server to retrieve all nearby points of interest.

2) I have a custom annotation as follows:

class CustomPointAnnotation: MKPointAnnotation {
    var imageName: String?
    var activeImageName : String?
    var trainCrossingId : Int?
    var notificationCount : UILabel = UILabel()
    var labelIsHidden : Bool = true
}

when I add the annotations onto the map, I am dynamically setting the notificationCount label to use data that I have retrieved from my Firebase Database.

3) A user is able to increase/decrease the size of the radius around their current location. With this new radius selected, I use mapView.removeAnnotations(mapView.annotations) to remove all annotations and then re-add these annotations with the new data retrieved from the server using the selected radius.

The Issue:

When initially loading the view, the annotations are working as expected with the correct labels set etc.

However, once a user updates the radius and I remove/add the previous annotations, the annotations and their corresponding labels are not showing as expected.

For example, this is what the map looks like when first entering into the view:

enter image description here

And then once I change the size of the radius: enter image description here

The expected data is what is shown in the initial image (when first entering the view), but when the radius is updated the z-value of my annotations are not respected, and notificationCount is not correct (eg, the third point found in the second diagram should not have a label). This is weird as I have set up print statements and break points to monitor each annotation in func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {...} and the values are correct and what I expect, however the view is not displaying these values.

Any ideas as to what might be going wrong here? Thanks in advance!

Edit to include the following mapView delegate:

 // Custom annotation view
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

        if !(annotation is CustomPointAnnotation) {
            return nil
        }

        let reuseId = "trainPin"

        var anView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
        if anView == nil {
            anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        }
        else {
            anView?.annotation = annotation
        }

        let cpa = annotation as! CustomPointAnnotation
        let icon : UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
        icon.image = UIImage(named:cpa.imageName)
        icon.layer.zPosition = 1
        anView?.rightCalloutAccessoryView = cpa.annotationButton
        anView?.addSubview(cpa.notificationCount)
        anView?.addSubview(icon)
        anView?.frame = CGRect(x: 0, y:0, width:32, height:32)
        anView?.canShowCallout = true
        return anView
    }

Solution

  • I believe that as anView will be reused, the view will have many subviews as you remove and add annotations. Try to remove the subviews first then add again:

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if !(annotation is CustomPointAnnotation) {
            return nil
        }
    
        let reuseId = "trainPin"
    
        var anView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
        if anView == nil {
            anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        }
        else {
            anView?.annotation = annotation
        }
    
        let cpa = annotation as! CustomPointAnnotation
        let icon : UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
        icon.image = UIImage(named:cpa.imageName)
        icon.layer.zPosition = 1
        anView?.rightCalloutAccessoryView = cpa.annotationButton
        for view in anView?.subviews {
            view.removeFromSuperView()
        }
        anView?.addSubview(cpa.notificationCount)
        anView?.addSubview(icon)
        anView?.frame = CGRect(x: 0, y:0, width:32, height:32)
        anView?.canShowCallout = true
        return anView
    }