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.
Related
Can any one help me to add different button action to the second annotation /pin (annotation2), Now the button do the same work in the two annotation pins how to do different work to each other . I'am using Swift3 in my project and this is my code . thanks
This is my code.
import UIKit
import MapKit
import CoreLocation
class MyAnnotation: MKPointAnnotation {
var uniqueId: Int!
}
class LocationViewController: UIViewController , MKMapViewDelegate , CLLocationManagerDelegate{
#IBOutlet weak var map: MKMapView!{
didSet{
map.delegate = self
}
}
#IBOutlet weak var locationInfo: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let locations = CLLocationCoordinate2DMake(33.314627, 44.303500)
let location2 = CLLocationCoordinate2DMake(33.312149, 44.3024567)
let span = MKCoordinateSpanMake(0.02, 0.02)
let span2 = MKCoordinateSpanMake(0.02, 0.02)
let region = MKCoordinateRegionMake(locations, span)
let region2 = MKCoordinateRegionMake(location2, span2)
map.setRegion(region, animated: true)
map.setRegion(region2, animated: true)
let annotation = MyAnnotation()
//annotation.setCoordinate(location)
annotation.coordinate = locations
annotation.title = "Zaid Homes"
annotation.subtitle = "Hay aljameaa"
annotation.uniqueId = 1
map.addAnnotation(annotation)
let annotation2 = MyAnnotation()
//annotation.setCoordinate(location)
annotation2.coordinate = location2
annotation2.title = "Zaid "
annotation2.subtitle = "aljameaa"
annotation.uniqueId = 2
map.addAnnotation(annotation2)
//Showing the device location on the map
self.map.showsUserLocation = true;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var view = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationView Id")
if view == nil{
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "AnnotationView Id")
view!.canShowCallout = true
} else {
view!.annotation = annotation
}
view?.leftCalloutAccessoryView = nil
view?.rightCalloutAccessoryView = UIButton(type: UIButtonType.detailDisclosure)
return view
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if (control as? UIButton)?.buttonType == UIButtonType.detailDisclosure {
mapView.deselectAnnotation(view.annotation, animated: false)
if let myAnnotation = view.annotation as? MyAnnotation {
if (myAnnotation.uniqueId == 1) {
performSegue(withIdentifier: "info", sender: view)
}
else {
performSegue(withIdentifier: "info2", sender: view)
}
}
}
}
}
The simplest way to know on which annotation you tap is using creating custom annotation class and adding annotation of it. So create one annotation class MyAnnotation child class of MKPointAnnotation and maintain one uniqueId with your multiple annotation.
class MyAnnotation: MKPointAnnotation {
var uniqueId: Int!
}
Now you need to add annotation of type MyAnnotation instead of MKPointAnnotation.
let annotation = MyAnnotation()
annotation.coordinate = locations
annotation.title = "Zaid Homes"
annotation.subtitle = "Hay aljameaa"
//Set uniqueId for annotation
annotation.uniqueId = 1
map.addAnnotation(annotation)
let annotation2 = MyAnnotation()
annotation2.coordinate = location2
annotation2.title = "Zaid "
annotation2.subtitle = "aljameaa"
//Set uniqueId for annotation
annotation2.uniqueId = 2
map.addAnnotation(annotation2)
Now check this uniqueId in calloutAccessoryControlTapped method on which annotation you tapped.
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if (control as? UIButton)?.buttonType == UIButtonType.detailDisclosure {
mapView.deselectAnnotation(view.annotation, animated: false)
if let myAnnotation = view.annotation as? MyAnnotation {
if (myAnnotation.uniqueId == 1) {
performSegue(withIdentifier: "info1", sender: view)
}
else {
performSegue(withIdentifier: "info2", sender: view)
}
}
}
}
you can do this by creating two subClass of MKPointAnnotation and then in the delegate's method you can do this :
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if view is subClass1 {
// do action for subclass 1
}
else if view is subClass2 {
// do action for subClass 2
}
}
Please let me know if this resolve your problem.
Update
you can make the implementation of you delegate more simpler like this exemple :
class ClassA:MKPointAnnotation{
func doActionWhenCalloutTapped(){
//do some action
}
}
class ClassB:ClassA{
override func doActionWhenCalloutTapped(){
//do some actions for annotation of type B
}
}
class ClassC:ClassA{
override func doActionWhenCalloutTapped(){
//do some actions for annotation of type C
}
}
func viewDidLoad(){
super.viewDidLoad()
let annotation = ClassB()
//annotation.setCoordinate(location)
annotation.coordinate = locations
annotation.title = "Zaid Homes"
annotation.subtitle = "Hay aljameaa"
map.addAnnotation(annotation)
let annotation2 = ClassC
//annotation.setCoordinate(location)
annotation2.coordinate = location2
annotation2.title = "Zaid "
annotation2.subtitle = "aljameaa"
map.addAnnotation(annotation2)
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
(view.annotation as! ClassA).doActionWhenCalloutTapped()
}
I'm new in Swift. I'm trying to have different color pin or custom pin on specific pin. My code works. I've a purple pin, but I want make a difference between them. How can I do it? I think there something to do in MapView delegate method but I didn't find it.
import UIKit
import MapKit
class MapsViewController: UIViewController , MKMapViewDelegate{
var shops: NSArray? {
didSet{
self.loadMaps()
}
}
#IBOutlet weak var map: MKMapView?
override func viewDidLoad() {
super.viewDidLoad()
loadMaps()
self.title = "Carte"
self.map!.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
// simple and inefficient example
let annotationView = MKPinAnnotationView()
annotationView.pinTintColor = UIColor.purpleColor()
return annotationView
}
func loadMaps(){
// navigationController?.navigationBar.topItem!.title = "Carte"
let shopsArray = self.shops! as NSArray
for shop in shopsArray {
let location = CLLocationCoordinate2D(
latitude: shop["lat"] as! Double,
longitude: shop["long"] as! Double
)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = shop["name"] as? String
annotation.subtitle = shop["addresse"] as? String
map?.addAnnotation(annotation)
}
// add point
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
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 pinColor, which you can use to customize the color of the annotation.
This example subclasses MKPointAnnotation:
import UIKit
import MapKit
class ColorPointAnnotation: MKPointAnnotation {
var pinColor: UIColor
init(pinColor: UIColor) {
self.pinColor = pinColor
super.init()
}
}
Create annotations of type ColorPointAnnotation and set their pinColor:
let annotation = ColorPointAnnotation(pinColor: UIColor.blueColor())
annotation.coordinate = coordinate
annotation.title = "title"
annotation.subtitle = "subtitle"
self.mapView.addAnnotation(annotation)
In viewForAnnotation, use the pinColor property to set the view's pinTintColor:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
let colorPointAnnotation = annotation as! ColorPointAnnotation
pinView?.pinTintColor = colorPointAnnotation.pinColor
}
else {
pinView?.annotation = annotation
}
return pinView
}
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.
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!
}
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.