Search code examples
iosswiftxcodemapkitmkannotation

Set MapKit pins with different colors


I am new in iOS and I implemented a MapKit with static markers from different arrays types and they are working fine , I am trying to put pins that come from shops array to be blue for example , and pins that come from community read , etc .. I have no idea about how to do this at all , they are all in red in the map and my goal is to change the color for every array of pins

here what I have tried :

import UIKit
import MapKit

class myMapViewController: UIViewController, MKMapViewDelegate {

    var shops = [Shops]()
    var communities = [Community]()
    var cyclists = [Cyclist]()
    var circuits = [Circuit]()
    
    @IBOutlet weak var myMap: MKMapView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
       
        shops.append(Shops(id: 0, title: "Shop1", latitude: 36.553015 , longitude: 10.592774))
        shops.append(Shops(id: 0, title: "Shop2", latitude: 35.499414 , longitude: 10.824846))
        communities.append(Community(id: 0, title: "community1", latitude: 37.276943 , longitude: 10.934709 ))
        communities.append(Community(id: 0, title: "community2", latitude: 35.427828 , longitude: 9.748186 ))
        circuits.append(Circuit(id: 0, title: "circuit1", latitude: 33.773035 , longitude: 10.857805 ))
        cyclists.append(Cyclist(id: 0, title: "cyclist1", latitude: 35.785118 , longitude: 10.000871 ))
        createShopsAnnotations(locations: shops)
        createCircuitsAnnotations(locations: circuits)
        createCommunityAnnotations(locations: communities)
        createCyclistsAnnotations(locations: cyclists)
    }
    
    func createShopsAnnotations(locations:[Shops]){
        for location in locations {
            let annotations = MKPointAnnotation()
            annotations.title = location.title as? String
            annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
            myMap.addAnnotation(annotations)
        }
    }
        
    func createCircuitsAnnotations(locations:[Circuit]){
        for location in locations {
            let annotations = MKPointAnnotation()
            annotations.title = location.title as? String
            annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
            myMap.addAnnotation(annotations)
        }
    }
    
    func createCommunityAnnotations(locations:[Community]){
        for location in locations {
            let annotations = MKPointAnnotation()
            annotations.title = location.title as? String
            annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
            myMap.addAnnotation(annotations)
        }
    }
    
    func createCyclistsAnnotations(locations:[Cyclist]){
        for location in locations {
            let annotations = MKPointAnnotation()
            annotations.title = location.title as? String
            annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
            
            myMap.addAnnotation(annotations)
        }
    }
}

I have headed some tutorials but I was not able to apply them on my example.


Solution

  • The basic idea is to make an annotation view type to render the correct color. So, assume for a second that you have four annotation types (see below), then you might have an annotation view type like so:

    class AnnotationView: MKMarkerAnnotationView {
        override var annotation: MKAnnotation? { didSet { update(for: annotation) } }
    
        override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
            super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
            update(for: annotation)
        }
    
        required init?(coder: NSCoder) {
            super.init(coder: coder)
        }
    
        func update(for annotation: MKAnnotation?) {
            switch annotation {
            case is Shop: markerTintColor = .blue
            case is Community: markerTintColor = .cyan
            case is Cyclist: markerTintColor = .green
            case is Circuit: markerTintColor = .red
            default: break
            }
        }
    }
    

    And then you would have your view controller register that annotation view for your map view:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        mapView.register(AnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
    
        ...
    }
    

    Note, when you register an annotation view type like this, you do not want to implement any mapView(_:viewFor:) method. Since iOS 11, that delegate method is not needed/recommended any more.

    Anyway, that annotation view type assumes you have four types of annotations. I'd personally make Shop, Community, Cyclist and Circuit just be annotation types, e.g.

    class Shop: NSObject, MKAnnotation {
        let id: Int
        dynamic var title: String?
        dynamic var subtitle: String?
        dynamic var coordinate: CLLocationCoordinate2D
    
        init(id: Int, title: String, subtitle: String? = nil, coordinate: CLLocationCoordinate2D) {
            self.id = id
            self.title = title
            self.subtitle = subtitle
            self.coordinate = coordinate
    
            super.init()
        }
    }
    
    // repeat for the other three types
    

    And then, when I want to add them to my map:

    shops.append(Shop(id: 0, title: "Shop1", coordinate: CLLocationCoordinate2D(latitude: 36.553015, longitude: 10.592774)))
    shops.append(Shop(id: 0, title: "Shop2", coordinate: CLLocationCoordinate2D(latitude: 35.499414 , longitude: 10.824846)))
    communities.append(Community(id: 0, title: "community1", coordinate: CLLocationCoordinate2D(latitude: 37.276943 , longitude: 10.934709)))
    communities.append(Community(id: 0, title: "community2", coordinate: CLLocationCoordinate2D(latitude: 35.427828 , longitude: 9.748186)))
    circuits.append(Circuit(id: 0, title: "circuit1", coordinate: CLLocationCoordinate2D(latitude: 33.773035 , longitude: 10.857805)))
    cyclists.append(Cyclist(id: 0, title: "cyclist1", coordinate: CLLocationCoordinate2D(latitude: 35.785118 , longitude: 10.000871)))
    
    mapView.addAnnotations(shops + communities + circuits + cyclists)
    

    Alternatively, you might have one annotation type:

    enum PlaceType {
        case shop, community, cyclist, circuit
    }
    
    class Place: NSObject, MKAnnotation {
        let id: Int
        let type: PlaceType
        dynamic var title: String?
        dynamic var subtitle: String?
        dynamic var coordinate: CLLocationCoordinate2D
    
        init(id: Int, type: PlaceType, title: String, subtitle: String? = nil, coordinate: CLLocationCoordinate2D) {
            self.id = id
            self.type = type
            self.title = title
            self.subtitle = subtitle
            self.coordinate = coordinate
    
            super.init()
        }
    }
    

    You'd then instantiate these places:

    var places = [Place]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        mapView.register(AnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
    
        places = [
            Place(id: 0, type: .shop, title: "Shop1", coordinate: CLLocationCoordinate2D(latitude: 36.553015, longitude: 10.592774)),
            Place(id: 0, type: .shop, title: "Shop2", coordinate: CLLocationCoordinate2D(latitude: 35.499414 , longitude: 10.824846)),
            Place(id: 0, type: .community, title: "community1", coordinate: CLLocationCoordinate2D(latitude: 37.276943 , longitude: 10.934709)),
            Place(id: 0, type: .community, title: "community2", coordinate: CLLocationCoordinate2D(latitude: 35.427828 , longitude: 9.748186)),
            Place(id: 0, type: .circuit, title: "circuit1", coordinate: CLLocationCoordinate2D(latitude: 33.773035 , longitude: 10.857805)),
            Place(id: 0, type: .cyclist, title: "cyclist1", coordinate: CLLocationCoordinate2D(latitude: 35.785118 , longitude: 10.000871))
        ]
        mapView.addAnnotations(places)
    }
    
    

    And your annotation view type would then use this type parameter:

    class AnnotationView: MKMarkerAnnotationView {
        override var annotation: MKAnnotation? { didSet { update(for: annotation) } }
    
        override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
            super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
            update(for: annotation)
        }
    
        required init?(coder: NSCoder) {
            super.init(coder: coder)
        }
    
        func update(for annotation: MKAnnotation?) {
            guard let annotation = annotation as? Place else { return }
    
            switch annotation.type {
            case .shop: markerTintColor = .blue
            case .community: markerTintColor = .cyan
            case .cyclist: markerTintColor = .green
            case .circuit: markerTintColor = .red
            }
        }
    }
    

    This results in:

    map