Display different images for every annotation on mapView in Swift - ios

Working with MapKit and Swift, I am trying to display a number of annotations with each having a unique image to display in the leftCalloutAccessoryView. I am able to display annotations with different titles and subtitles; however, the image that is displayed for every annotation is the same image - I am having trouble figuring out how to make every image unique. Much appreciation on any and all feedback/guidance to getting this to work. Code below (p.s. I am aware I have hard-coded an image; I am unsure how to iterate through each annotation to display the appropriate images):
override func viewDidLoad() {
super.viewDidLoad()
mySearch.delegate = self
theMapView.delegate = self
var latAustin:CLLocationDegrees = 30.274751
var lngAustin:CLLocationDegrees = -97.739141
var latAustinDelta:CLLocationDegrees = 0.1
var lngAustinDelta:CLLocationDegrees = 0.1
var theSpan:MKCoordinateSpan = MKCoordinateSpanMake(latAustinDelta, lngAustinDelta)
var austinLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latAustin, lngAustin)
var theRegion:MKCoordinateRegion = MKCoordinateRegionMake(austinLocation, theSpan)
theMapView.setRegion(theRegion, animated: true)
// println(theMapView.userLocation.coordinate.latitude)
// println(theMapView.userLocation.coordinate.longitude)
for var i = 0; i < politicians.count; i++ {
var politician = MKPointAnnotation()
var politicianTotalFunding:String = politicians[i]["totalFunding"]! as String
politician.coordinate = CLLocationCoordinate2DMake(politiciansCoords[i]["lat"]! as CLLocationDegrees, politiciansCoords[i]["lng"]! as CLLocationDegrees)
politician.title = politicians[i]["name"]! as String
politician.subtitle = "Total Funding: \(politicianTotalFunding)"
theMapView.addAnnotation(politician)
}
}
// Delegate method called each time an annotation appears in the visible window
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Red
var imageview = UIImageView(frame: CGRectMake(0, 0, 45, 45))
imageview.image = UIImage(named: politicians[0]["photo"])
pinView!.leftCalloutAccessoryView = imageview
pinView!.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as UIButton
} else {
pinView!.annotation = annotation
}
return pinView
}
Thanks again so much for all your help!

you need to create custom MKPointAnnotation class and then add those annotation . when view is called you can use custom variable defined in MKPointAnnotation as images.

Related

Add title and subtitle to associated annotations in swift 4

I'm showing an array of annotations in a map view and need to have the corresponding title and subtitle with each annotation. My current code only gets me the 1st title/subtitle on all of the annotations.
func multiPoint() {
var coordinateArray: [CLLocationCoordinate2D] = []
if receivedArrayOfLats.count == receivedArrayOfLongs.count {
for i in 0 ..< receivedArrayOfLats.count {
let eventLocation = CLLocationCoordinate2DMake(receivedArrayOfLats[i], receivedArrayOfLongs[i])
coordinateArray.append(eventLocation)
}
}
for events in coordinateArray {
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: events.latitude, longitude: events.longitude)
annotation.title = receivedAgencyEventSubTypeCode
annotation.subtitle = receivedAgencyId
multiEventMap?.addAnnotation(annotation)
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let reuseIdentifier = "annotationView"
var view = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if #available(iOS 11.0, *) {
if view == nil {
view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
view?.displayPriority = .required
} else {
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
}
view?.annotation = annotation
view?.canShowCallout = true
return view
}
No big surprise. You loop making annotations, and you are saying
annotation.title = receivedAgencyEventSubTypeCode
annotation.subtitle = receivedAgencyId
for every annotation thru the loop. The annotation is different each time. But the values on the right side never change so all the titles and subtitles are the same.

Callout action from Annotations in Map view

I have an array of latitudes and longitudes along with other stuffs and I want to create annotations in Map view. I’m using MapKit.
When ever the user clicks the annotation callout I’m going to present another controller. I’m not able to find the index of the array element that was used to create that particular annotation.
Below is my code.
I have a Int variable
var annotationIndex = 0
This is where I add annotations
func addAnnotation(latitude: Double, longitude: Double, title: String, subTitle: String?) {
if (latitude >= -90 && latitude <= 90) && (longitude >= -180 && longitude <= 180) {
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = title
annotation.subtitle = subTitle
mapView.addAnnotation(annotation)
}
}
This is an MKMapViewDelegate method to customize annotation
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
{
if !(annotation is MKPointAnnotation)
{
return nil
}
let reuseId = "test"
var aView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
aView.canShowCallout = true
let btn = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIButton
btn.addTarget(self, action: "btnDisclosureAction:", forControlEvents: UIControlEvents.TouchUpInside)
btn.tag = annotationIndex
annotationIndex++
aView.rightCalloutAccessoryView = btn
return aView
}
This is the rightCalloutAccessoryView Button Action
func btnDisclosureAction(sender: UIButton) {
println(sender.tag)
}
Right now I'm just printing the callout diusclosure button tag which I try setting it using an annotationIndex variable.
Since the annotationView is reused I cannot get the exact index value.Is there a better way to do it?
Subclass MKPointAnnotation to have index value.
class
Annotation : MKPointAnnotation {
var index = 0
}
And set index when annotation is created.
let annotation = Annotation()
:
annotation.subtitle = subTitle
annotation.index = annotationIndex++
Pass annotation's index to btn
btn.addTarget(self, action: "btnDisclosureAction:", forControlEvents: UIControlEvents.TouchUpInside)
btn.tag = ( annotation as! Annotation ).index
EDIT:
So your viewForAnnotation will be
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
{
if !(annotation is Annotation)
{
return nil
}
let reuseId = "test"
var aView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
aView.canShowCallout = true
let btn = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIButton
btn.addTarget(self, action: "btnDisclosureAction:", forControlEvents: UIControlEvents.TouchUpInside)
btn.tag = ( annotation as! Annotation ).index
aView.rightCalloutAccessoryView = btn
return aView
}
Specify indexselected as class variable and initilise it with -1 in viewDidLoad method ,like
override func viewDidLoad() {
super.viewDidLoad()
indexselected = -1
}
You can get your annotation index while user tap on annotation, and didSelectAnnotationView method get called automatically.
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
var selectedAnnotation : MKPointAnnotation = MKPointAnnotation()
selectedAnnotation = view.annotation as! MKPointAnnotation
for var i = 0; i<yourlist.count ; i++ {
let strtitle : String = yourlist.objectAtIndex(i).title as! String
if selectedAnnotation.title == strtitle {
indexselected=i
break
}
}
}
We can get annotation index by comparing title on annotation and title from your array, and save the index from array. On selection of button method you can use that index.
func btnDisclosureAction(sender: UIButton) {
println(indexselected)
}
Hope this help you.

Change pin image on MKMapView in Swift

I am trying to change pin image on the MKMapView in Swift, but unfortunately it don't work. Any Idea what I am doing wrong ? I saw some examples here, but did not worked.
import UIKit
import MapKit
class AlarmMapViewController: UIViewController {
#IBOutlet weak var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
showAlarms()
map.showsUserLocation = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showAlarms(){
map.region.center.latitude = 49
map.region.center.longitude = 12
map.region.span.latitudeDelta = 1
map.region.span.longitudeDelta = 1
for alarm in Alarms.sharedInstance.alarms {
let location = CLLocationCoordinate2D(
latitude: Double(alarm.latitude),
longitude: Double(alarm.longtitude)
)
let annotation = MKPointAnnotation()
annotation.setCoordinate(location)
annotation.title = alarm.name
annotation.subtitle = alarm.description
mapView(map, viewForAnnotation: annotation).annotation = annotation
map.addAnnotation(annotation)
}
}
#IBAction func zoomIn(sender: AnyObject) {
}
#IBAction func changeMapType(sender: AnyObject) {
}
func mapView(mapView: MKMapView!,
viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.image = UIImage(named:"GreenDot")!
}
else {
pinView!.annotation = annotation
}
return pinView
}
}
GreenDot picture is available and used on other places.
Don't forget to set:
map.delegate = self
And make sure your UIViewController implements the MKMapViewDelegate protocol.
If you forget to do this, your implementation of mapView:viewForAnnotation: won't be invoked for your map.
Besides, it looks like pinView!.animatesDrop = true breaks custom images. You'd have to set it to false, or use MKAnnotationView (which doesn't have an animatesDrop property).
See this related question if you want to implement a custom drop animation.
MKPinAnnotationView always use a pin image, can't be everride. You must use MKAnnotationView instead.
Be aware because property animatesDrop it's not a valid MKAnnotationView's property, Rem the line.

Swift different images for Annotation

I managed to get a custom icon for a annotation pin in Swift, but now I am still stuck using 2 different images for different annotations. Right now a button adds a annotation to the map. There should be another button that also adds a annotation but with another icon.
Is there a way to use the reuseId for this?
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var Map: MKMapView!
#IBAction func btpressed(sender: AnyObject) {
var lat:CLLocationDegrees = 40.748708
var long:CLLocationDegrees = -73.985643
var latDelta:CLLocationDegrees = 0.01
var longDelta:CLLocationDegrees = 0.01
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, long)
var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
Map.setRegion(region, animated: true)
var information = MKPointAnnotation()
information.coordinate = location
information.title = "Test Title!"
information.subtitle = "Subtitle"
Map.addAnnotation(information)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if !(annotation is MKPointAnnotation) {
return nil
}
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.image = UIImage(named:"1.png")
anView.canShowCallout = true
}
else {
anView.annotation = annotation
}
return anView
}
In the viewForAnnotation delegate method, set the image based on which annotation the method is being called for.
Be sure to do this after the view is dequeued or created (and not only in the if anView == nil part). Otherwise, annotations that use a dequeued view will show the image of the annotation that used the view previously.
With the basic MKPointAnnotation, one crude way to tell annotations apart is by their title but that's not very flexible.
A better approach is to use a custom annotation class that implements the MKAnnotation protocol (an easy way to do that is to subclass MKPointAnnotation) and add whatever properties are needed to help implement the custom logic.
In the custom class, add a property, say imageName, which you can use to customize the image based on the annotation.
This example subclasses MKPointAnnotation:
class CustomPointAnnotation: MKPointAnnotation {
var imageName: String!
}
Create annotations of type CustomPointAnnotation and set their imageName:
var info1 = CustomPointAnnotation()
info1.coordinate = CLLocationCoordinate2DMake(42, -84)
info1.title = "Info1"
info1.subtitle = "Subtitle"
info1.imageName = "1.png"
var info2 = CustomPointAnnotation()
info2.coordinate = CLLocationCoordinate2DMake(32, -95)
info2.title = "Info2"
info2.subtitle = "Subtitle"
info2.imageName = "2.png"
In viewForAnnotation, use the imageName property to set the view's image:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
}
else {
anView.annotation = annotation
}
//Set annotation-specific properties **AFTER**
//the view is dequeued or created...
let cpa = annotation as CustomPointAnnotation
anView.image = UIImage(named:cpa.imageName)
return anView
}
iOS Swift Code With Help of Anna and Fabian Boulegue:
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
var info1 = CustomPointAnnotation()
info1.coordinate = CLLocationCoordinate2DMake(26.889281, 75.836042)
info1.title = "Info1"
info1.subtitle = "Subtitle"
info1.imageName = "flag.png"
var info2 = CustomPointAnnotation()
info2.coordinate = CLLocationCoordinate2DMake(26.862280, 75.815098)
info2.title = "Info2"
info2.subtitle = "Subtitle"
info2.imageName = "flag.png"
mapView.addAnnotation(info1)
mapView.addAnnotation(info2)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
println("delegate called")
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
}
else {
anView.annotation = annotation
}
//Set annotation-specific properties **AFTER**
//the view is dequeued or created...
let cpa = annotation as CustomPointAnnotation
anView.image = UIImage(named:cpa.imageName)
return anView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class CustomPointAnnotation: MKPointAnnotation {
var imageName: String!
}

Swift Annotation CallOut Button selection

I have a mapView with a custom annotation. I also added a button to this but is there a way to see on which annotation the button was pressed?
So the post to the console should not only be "Disclosure Pressed!". I would need something like "Disclosure Pressed! info(X).subtitle" to see the user taped info1 or info2.
Here is the code:
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var Map: MKMapView!
class CustomPointAnnotation: MKPointAnnotation {
var imageName: String!
}
#IBAction func btpressed(sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
//1
var lat1:CLLocationDegrees = 40.748708
var long1:CLLocationDegrees = -73.985643
var latDelta1:CLLocationDegrees = 0.01
var longDelta1:CLLocationDegrees = 0.01
var span1:MKCoordinateSpan = MKCoordinateSpanMake(latDelta1, longDelta1)
var location1:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat1, long1)
var region1:MKCoordinateRegion = MKCoordinateRegionMake(location1, span1)
Map.setRegion(region1, animated: true)
var info1 = CustomPointAnnotation()
info1.coordinate = location1
info1.title = "Test Title1!"
info1.subtitle = "Subtitle1"
info1.imageName = "1.png"
Map.addAnnotation(info1)
//2
var lat2:CLLocationDegrees = 41.748708
var long2:CLLocationDegrees = -72.985643
var latDelta2:CLLocationDegrees = 0.01
var longDelta2:CLLocationDegrees = 0.01
var span2:MKCoordinateSpan = MKCoordinateSpanMake(latDelta2, longDelta2)
var location2:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat2, long2)
var region2:MKCoordinateRegion = MKCoordinateRegionMake(location2, span2)
var info2 = CustomPointAnnotation()
info2.coordinate = location2
info2.title = "Test Title2!"
info2.subtitle = "Subtitle2"
info2.imageName = "2.png"
Map.addAnnotation(info2)
}
func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == annotationView.rightCalloutAccessoryView {
println("Disclosure Pressed!")
}
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if !(annotation is CustomPointAnnotation) {
return nil
}
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.canShowCallout = true
anView.rightCalloutAccessoryView = UIButton.buttonWithType(.InfoDark) as UIButton
var imageview = UIImageView(frame: CGRectMake(0, 0, 10, 10))
imageview.image = UIImage(named: "1.png")
anView!.leftCalloutAccessoryView = imageview
} else {
anView.annotation = annotation
}
//Set annotation-specific properties **AFTER**
//the view is dequeued or created...
let cpa = annotation as CustomPointAnnotation
anView.image = UIImage(named:cpa.imageName)
return anView
}
}
I've already tried something like:
if control == annotationView.rightCalloutAccessoryView {
println("Disclosure Pressed!" \(self.subtitle))
}
In the calloutAccessoryControlTapped delegate method, self is not the annotation whose callout accessory view was tapped.
self refers to the instance of the class the method is contained in (i.e. an instance of ViewController). The other issue is that in the println, you put the variable reference outside the quotes instead of inside.
The delegate method gives you a reference to the annotation view: the annotationView argument.
The annotation view contains a reference to the annotation: annotationView.annotation.
However, please note your delegate method declaration differs slightly from the documented one (annotationView argument has a local name view and other arguments are marked as optional with the ! suffix).
It's better to use the documented declaration even though your version will technically still work.
Complete example below also shows how to check if the annotation is one of your custom objects and how to access the custom property:
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!,
calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView {
println("Disclosure Pressed! \(view.annotation.subtitle)")
if let cpa = view.annotation as? CustomPointAnnotation {
println("cpa.imageName = \(cpa.imageName)")
}
}
}
Side note:
Since you've set the leftCalloutAccessoryView to a UIImageView, tapping on it will not call calloutAccessoryControlTapped because it is only called for views that are subclasses of UIControl. UIImageView is not a subclass of UIControl. If you need to detect taps on the left accessory, create a custom UIButton (and set its image) instead of a UIImageView.

Resources