1
votes

I am developing an app in which I should present MapView annotations showing an image and a title. The following View Controller Swift code shows a default pin image with the desired title right below:

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    @IBOutlet weak var mapView: MKMapView!

    var locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.delegate = self
        locationManager.delegate = self

        // Define zoom
        let deltaLat: CLLocationDegrees = 1.0
        let deltaLon: CLLocationDegrees = 1.0

        // Define location of center coordinates
        let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(-15.3, -47.0)        

        // Define area to be viwed
        let areaVisual: MKCoordinateSpan = MKCoordinateSpanMake(deltaLat, deltaLon)
        let region = MKCoordinateRegionMake(location, areaVisual)
        let annotation = MKPointAnnotation()   
        annotation.coordinate = location
        annotation.title = "SDKP"
        mapView.addAnnotation(annotation)

        // Show map region defined by the above parameters
        mapView.setRegion(region, animated: true)
    }

    /*
    // Show an image for annotation
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: nil)
        annotationView.image =  imageLiteral(resourceName: "AnnotationImage")
        return annotationView
    }
    */
}

This is the MapView I get with this:

enter image description here

When I un-comment the view for annotation method, I get the desired annotation image, but not the title:

enter image description here

Any ideas on how can I get both the image and title at the same time for the annotation?

2

2 Answers

2
votes

I found a solution in which I use the func imageFromLabel(_:) in code below to extend UIImage to create an image from a label text which is the title for the annotation. Then I combine the annotation image with this title image through the func combineImageAndTitle(_:_:). Finally, this combined image is showed by the mapView delegate method viewFor annotation.

Since I am still a beginner with Swift, I am not sure if it is the best way to do that. But this solution is working fine for me.

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    @IBOutlet weak var mapView: MKMapView!

    var locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.delegate = self
        locationManager.delegate = self

        // Define zoom
        let deltaLat: CLLocationDegrees = 1.0
        let deltaLon: CLLocationDegrees = 1.0

        // Define location of center coordinates
        let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(-15.3, -47.0)

        // Define area to be viwed
        let areaVisual: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: deltaLat, longitudeDelta: deltaLon)
        let region = MKCoordinateRegion(center: location, span: areaVisual)
        let annotation = MKPointAnnotation()
        annotation.coordinate = location
        annotation.title = "SDKP"
        mapView.addAnnotation(annotation)

        // Show map region defined by the above parameters
        mapView.setRegion(region, animated: true)
    }


    // Delegate method for mapView
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: nil)
        let imageForAnnotation = #imageLiteral(resourceName: "BaseImage")
        let annotationTitle = (annotation.title ?? "") ?? ""
        //annotationView.image = imageForAnnotation
        annotationView.image = combineImageAndTitle(image: imageForAnnotation, title: annotationTitle)
        return annotationView
    }


    /// Combine image and title in one image.
    func combineImageAndTitle(image: UIImage, title: String) -> UIImage {
        // Create an image from ident text
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
        label.numberOfLines = 1
        label.textAlignment = .center
        label.textColor = UIColor.black
        label.text = title
        let titleImage = UIImage.imageFromLabel(label: label)

        // Resulting image has a 100 by 100 size
        let contextSize = CGSize(width: 100, height: 100)

        UIGraphicsBeginImageContextWithOptions(contextSize, false, UIScreen.main.scale)

        let rect1 = CGRect(x: 50 - Int(image.size.width / 2), y: 50 - Int(image.size.height / 2), width: Int(image.size.width), height: Int(image.size.height))
        image.draw(in: rect1)

        let rect2 = CGRect(x: 0, y: 53 + Int(image.size.height / 2), width: Int(titleImage.size.width), height: Int(titleImage.size.height))
        titleImage.draw(in: rect2)

        let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return combinedImage!
    }

}


extension UIImage {
    /// Convert a label to an image
    class func imageFromLabel(label: UILabel) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
        label.layer.render(in: UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }
}

And this is the resulting MapView.

1
votes

You can use MKMarkerAnnotationView and glyphImage property. Try the following code

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: nil)
    annotationView.glyphImage = UIImage(named: "Laugh")
    return annotationView
}