Swift - Custom MKPointAnnotation with Image
Solution 1
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
from MKMapViewDelegate
is the function you need to override.
It allows you to provide "custom" views for the each annotation. Within this function, you can either dequeue a custom view (subclass of MKAnnotationView
) and set custom properties OR you can dequeue a regular MKAnnotationView
which has a property image
.
You can set that property to display custom images. I'd rather use my own annotationView anyway as you can add custom layouts (labels, imageViews, etc..) and themes (colours, layers, etc..).
Example:
//
// ViewController.swift
// Maps
//
// Created by Brandon T on 2017-02-20.
// Copyright © 2017 XIO. All rights reserved.
//
import UIKit
import MapKit
class ImageAnnotation : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var image: UIImage?
var colour: UIColor?
override init() {
self.coordinate = CLLocationCoordinate2D()
self.title = nil
self.subtitle = nil
self.image = nil
self.colour = UIColor.white
}
}
class ImageAnnotationView: MKAnnotationView {
private var imageView: UIImageView!
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
self.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
self.addSubview(self.imageView)
self.imageView.layer.cornerRadius = 5.0
self.imageView.layer.masksToBounds = true
}
override var image: UIImage? {
get {
return self.imageView.image
}
set {
self.imageView.image = newValue
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ViewController: UIViewController, MKMapViewDelegate {
var mapView: MKMapView!
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
self.initControls()
self.doLayout()
self.loadAnnotations()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func initControls() {
self.mapView = MKMapView()
self.mapView.isRotateEnabled = true
self.mapView.showsUserLocation = true
self.mapView.delegate = self
let center = CLLocationCoordinate2DMake(43.761539, -79.411079)
let region = MKCoordinateRegionMake(center, MKCoordinateSpanMake(0.005, 0.005))
self.mapView.setRegion(region, animated: true)
}
func doLayout() {
self.view.addSubview(self.mapView)
self.mapView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.mapView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
self.mapView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.mapView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.mapView.translatesAutoresizingMaskIntoConstraints = false
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKind(of: MKUserLocation.self) { //Handle user location annotation..
return nil //Default is to let the system handle it.
}
if !annotation.isKind(of: ImageAnnotation.self) { //Handle non-ImageAnnotations..
var pinAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "DefaultPinView")
if pinAnnotationView == nil {
pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "DefaultPinView")
}
return pinAnnotationView
}
//Handle ImageAnnotations..
var view: ImageAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: "imageAnnotation") as? ImageAnnotationView
if view == nil {
view = ImageAnnotationView(annotation: annotation, reuseIdentifier: "imageAnnotation")
}
let annotation = annotation as! ImageAnnotation
view?.image = annotation.image
view?.annotation = annotation
return view
}
func loadAnnotations() {
let request = NSMutableURLRequest(url: URL(string: "https://i.imgur.com/zIoAyCx.png")!)
request.httpMethod = "GET"
let session = URLSession(configuration: URLSessionConfiguration.default)
let dataTask = session.dataTask(with: request as URLRequest) { (data, response, error) in
if error == nil {
let annotation = ImageAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(43.761539, -79.411079)
annotation.image = UIImage(data: data!, scale: UIScreen.main.scale)
annotation.title = "Toronto"
annotation.subtitle = "Yonge & Bloor"
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
}
}
}
dataTask.resume()
}
}
Solution 2
If you want set in map many location with different pictures use this code:
@Brandon, Thank you
@objc func loadAnnotations() {
for item in locations{
DispatchQueue.main.async {
let request = NSMutableURLRequest(url: URL(string: "<YourPictureUrl>")!)
request.httpMethod = "GET"
let session = URLSession(configuration: URLSessionConfiguration.default)
let dataTask = session.dataTask(with: request as URLRequest) { (data, response, error) in
if error == nil {
let annotation = ImageAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(Double(item["pl_lat"] as! String)!,
Double(item["pl_long"] as! String)!)
annotation.image = UIImage(data: data!, scale: UIScreen.main.scale)
annotation.title = "T"
annotation.subtitle = ""
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
}
}
}
dataTask.resume()
}
}
}
user2397282
Updated on June 04, 2022Comments
-
user2397282 almost 2 years
I am trying to create a custom
MKPointAnnotation
to use on a map view. It looks very much like the one used in Apple's Photos:I will be retrieving a number of photos from a server, along with their location. I then want to display an annotation like the one above, with the image inside the annotation.
I currently have a program that can add a normal
MKPointAnnotation
at the right coordinate, and can also retrieve the relevant photo from the server.All I want is to style my
MKPointAnnotation
to look just like that.I have tried following other answers but I think this is slightly different because I want to show an image on a template every time.
-
user2397282 about 7 yearsSorry, I'm a bit confused. How do I create the custom annotation with this implementation?
-
Brandon about 7 yearsImageAnnotationView is a custom annotation view. You need to customize it however you want. Set it's background to white, add the image, etc..
-
user2397282 about 7 yearsOh I see, so I won't use an MKPointAnnotation?
-
Brandon about 7 years@user2397282 you still would. MKAnnotation (MKAnnotationPoint) is completely different from MKAnnotationView. MKAnnotation holds information about the location on a map. MKAnnotationView decides how it appears or looks.
-
user2397282 about 7 yearsOkay, so I would just create an MKAnnotationPoint in the normal way (to make a red pin) and then style it with the MKAnnotationView? And do I do that inside the mapView method?
-
Brandon about 7 yearsWhen you add annotations to the mapView, it calls the viewForAnnotation method as shown above. You do all your theming in that method using the annotation parameter. You return the customized view as I've shown above.
-
Brandon about 7 years@user2397282 I added an example. You can see how it works by running it.
-
Mirko over 6 yearsI do not get the image to show, just the red pin, and I copied and pasted the code to my controller...
-
Edison Lo over 5 yearsSmart solution, as you can not create a custom MKAnnotationView with NIB on your own