Tigran Iskandaryan
Tigran Iskandaryan

Reputation: 1451

Adding cluster annotations to a map view, which has several custom annotation views

I have a map view, which has two types of custom annotation views. I am wondering how to add different types of clusters for these views (depending on the types of annotation views). Currently I've tried to do everything as in the sample project from WWDC 2017's session 237 "What's new in MapKit". But when I register my cluster view, it does nothing (even not get called). I guess, that is because I use custom annotation views and don't register them, but instead, use MKMapViewDelegate method mapView(_:viewFor:). Here is the code where I register my custom cluster annotation (ClusterView is a subclass of MKAnnotationView where I define my cluster annotation):

 mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)

The code above is defined inside a viewDidLoad() method, but again, it doesn't even get called. So, I think I should implement one of MKMapViewDelegate methods: mapView(_:clusterAnnotationForMemeberAnnotations:). The problem is, I don't have any experience in adding cluster annotations, so I don't know how to implement it right. I've been looking for some examples on the Internet for several weeks but haven't found anything yet (only about third party libraries). If you know how to implement the above mentioned method, or other way of adding clusters to a mapView that has different types of custom annotation views (without using third party libraries), I would appreciate your help.

Upvotes: 3

Views: 6229

Answers (1)

Cory
Cory

Reputation: 351

Try to register your Annotations with String reuse Identifier:

mapView.register(AnnotationViewMarker.self, forAnnotationViewWithReuseIdentifier: "marker")
mapView.register(AppleClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: "cluster")

I already had this problem too. In case here is an example that work in my app:

override func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {        
    if annotation is MKUserLocation || annotation is UserAnnotation {
        return nil
    }

    if let marker = annotation as? WifiAnnotation {
        var view = mapView.dequeueReusableAnnotationView(withIdentifier: "marker") as? AnnotationViewMarker
        if view == nil {
            view = AnnotationViewMarker(annotation: marker, reuseIdentifier: "marker")
        }
        return view

    } else if let cluster = annotation as? MKClusterAnnotation {
        var view = mapView.dequeueReusableAnnotationView(withIdentifier: "cluster") as? AppleClusterAnnotationView
        if view == nil {
            view = AppleClusterAnnotationView(annotation: cluster, reuseIdentifier: "cluster")
        }
        return view

    }

    return nil
}

here an exemple of my MKAnnotationView Class:

class AppleClusterAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    displayPriority = .defaultLow
    //collisionMode = .circle
    centerOffset = CGPoint(x: 0, y: -10) // Offset center point to animate better with marker annotations
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override var annotation: MKAnnotation? {
    willSet {
        if let cluster = newValue as? MKClusterAnnotation {
            let renderer = UIGraphicsImageRenderer(size: CGSize(width: 40, height: 40))
            let count = cluster.memberAnnotations.count
            let uniCount = cluster.memberAnnotations.filter { member -> Bool in
                return (member as! WifiAnnotation).wifiType != "Ad"
                }.count
            image = renderer.image { _ in
                // Fill full circle with tricycle color
                UIColor(red: 52/255, green: 131/255, blue: 223/255, alpha: 0.22).setFill()
                UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 40, height: 40)).fill()

                // Fill pie with unicycle color
                UIColor(red: 52/255, green: 131/255, blue: 223/255, alpha: 0.22).setFill()
                let piePath = UIBezierPath()
                piePath.addArc(withCenter: CGPoint(x: 20, y: 20), radius: 20,
                               startAngle: 0, endAngle: (CGFloat.pi * 2.0 * CGFloat(uniCount)) / CGFloat(count),
                               clockwise: true)
                piePath.addLine(to: CGPoint(x: 20, y: 20))
                piePath.close()
                piePath.fill()

                // Fill inner circle with white color
                UIColor.white.setFill()
                UIBezierPath(ovalIn: CGRect(x: 8, y: 8, width: 24, height: 24)).fill()

                // Finally draw count text vertically and horizontally centered
                let attributes = [ NSAttributedStringKey.foregroundColor: UIColor.black,
                                   NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 15)]
                let text = "\(count)"
                let size = text.size(withAttributes: attributes)
                let rect = CGRect(x: 20 - size.width / 2, y: 20 - size.height / 2, width: size.width, height: size.height)
                text.draw(in: rect, withAttributes: attributes)

            }
        }
    }
}}

And finally my MKMarkerAnnotationView. With that you are all setup to make it works. You only have to setup your MKAnnotation.

MKMarkerAnnotationView:

class AnnotationViewMarker: MKMarkerAnnotationView {

override open var annotation: MKAnnotation? {
    willSet {
        if let annotation = newValue as? WifiAnnotation {
            clusteringIdentifier = "WifiAnnotation"

            if annotation.wifiType == "yyy" {
                glyphImage = UIImage(named: "yyy")
                selectedGlyphImage = UIImage(named: "yyy")
                markerTintColor = UIColor("#303030")
                glyphTintColor = .white
                displayPriority = .defaultHigh
                titleVisibility = .visible
                animatesWhenAdded = true


            } else {
                glyphImage = UIImage(named: "xxx")
                selectedGlyphImage = UIImage(named: "xxx")
                markerTintColor = UIColor("#3483DF")
                glyphTintColor = .white
                displayPriority = .required
                titleVisibility = .visible
                animatesWhenAdded = true

                let accessButton = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
                accessButton.setImage(self.accessAnnotation, for: .normal) //UIImage(named: "accessAnnotation"), for: .normal)
                rightCalloutAccessoryView = accessButton
            }

            //collisionMode = .circle
        }

    }
}

}

Upvotes: 4

Related Questions