I have a custom annotation for my mapView. I initially set the coordinate, title (eg. "first title"), subTitle (eg. "first address"), userId, and a distance (eg. 0 meters) property on it with some data. I add it to the mapView and to an array for later use. Everything works, it shows on the mapView, I press it and the callout shows that initial data.
I later get updated that the location for that callout has changed. I loop through the array and update the callout with new data for the coordinate, title (eg. "new title"), subTitle (eg. "new address"), and distance (eg. 100 meters) properties. I also animate the callout from it's original location to it's new location. The animation works fine and the callout moves from point A to point B.
The problem is when I tap the annotation the old data gets shown on the callout instead of the new data.
I use calloutAccessoryControlTapped to push on a new vc. When i put a breakpoint there the custom pin has all the new data. The error seems to happen with the callout.
How do I fix this?
I don't want to clear all the annotations from the mapView so that's not an option. I call mapView.removeAnnotation(customPin) and mapView.addAnnotation(customPin) which fixes the problem for that pin but there is a blink when the pin is removed and added back to the map and then when it animates to it's new location it looks choppy.
Custom Annotation
class CustomPin: NSObject, MKAnnotation {
#objc dynamic var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var userId: String?
var distance: CLLocationDistance?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, userId: String, distance: CLLocationDistance?) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.userId = userId
self.distance = distance
super.init()
}
}
First time the annotation gets set with initial data
firstFunctionThatGetsTheInitialLocation(origLat, origLon) {
let firstCoordinate = CLLocationCoordinate2DMake(origLat, origLon)
let distanceInMeters: CLLocationDistance = self.center.distance(from: anotherUsersLocation)
let customPin = CustomPin(coordinate: firstCoordinate, title: "first title", subtitle: "first address", userId: "12345", distance: distance)
DispatchQueue.main.async { [weak self] in
self?.mapView.addAnnotation(customPin)
self?.arrOfPins.append(customPin)
}
}
Second time the annotation gets set with New Data
secondFunctionThatGetsTheNewLocation(newCoordinate: CLLocationCoordinate2D, newDistance: CLLocationDistance) {
for pin in customPins {
pin.title = "second title" // ** updates but the callout doesn't reflect it
pin.subTitle = "second address" // ** updates but the callout doesn't reflect it
pin.distance = newDistance // ** updates but the callout doesn't reflect it
// calling these gives me the new data but the annotation blinks and moves really fast to it's new location
// mapView.removeAnnotation(pin)
// mapView.addAnnotation(pin)
UIView.animate(withDuration: 1) {
pin.coordinate = newCoordinate // this updates and animates to the new location with no problem
}
}
}
MapView viewFor annotation
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKind(of: MKUserLocation.self) { return nil }
guard let annotation = annotation as? CustomPin else { return nil }
let reuseIdentifier = "CustomPin"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
annotationView?.canShowCallout = true
annotationView?.calloutOffset = CGPoint(x: -5, y: 5)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView?.image = UIImage(named: "chevronImage")
} else {
annotationView?.annotation = annotation
}
annotationView?.detailCalloutAccessoryView = nil
annotationView?.detailCalloutAccessoryView = createCallOutWithDataFrom(customPin: annotation)
return annotationView
}
Creation of UIView for Callout
func createCallOutWithDataFrom(customPin: CustomPin) -> UIView {
let titleText = customPin.title
let subTitleText = customPin.subTitle
let distanceText = subTitle.distance // gets converted to a string
// 1. create a UIView
// 2. create some labels and add the text from the title, subTitle, and distance and add them as subViews to the UIView
// 3. return the UIView
}
There are a few issues:
You need to use the #objc dynamic qualifier for any properties you want to observe. The standard callout performs Key-Value Observation (KVO) on title and subtitle. (And the annotation view observes changes to coordinate.)
If you want to observe userid and distance, you have to make those #objc dynamic as well. Note, you’ll have to make distance be non-optional to make that observable:
var distance: CLLocationDistance
So:
class CustomAnnotation: NSObject, MKAnnotation {
// standard MKAnnotation properties
#objc dynamic var coordinate: CLLocationCoordinate2D
#objc dynamic var title: String?
#objc dynamic var subtitle: String?
// additional custom properties
#objc dynamic var userId: String
#objc dynamic var distance: CLLocationDistance
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, userId: String, distance: CLLocationDistance) {
self.userId = userId
self.distance = distance
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
super.init()
}
}
Like I said, the standard callout observes title and subtitle. While you have to make the annotation properties observable, if you’re going to build your own detailCalloutAccessoryView, you’re going to have to do your own KVO:
class CustomAnnotationView: MKMarkerAnnotationView {
private let customClusteringIdentifier = "..."
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
canShowCallout = true
detailCalloutAccessoryView = createCallOutWithDataFrom(customAnnotation: annotation as? CustomAnnotation)
clusteringIdentifier = customClusteringIdentifier
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
removeAnyObservers()
}
override var annotation: MKAnnotation? {
didSet {
removeAnyObservers()
clusteringIdentifier = customClusteringIdentifier
if let customAnnotation = annotation as? CustomAnnotation {
updateAndAddObservers(for: customAnnotation)
}
}
}
private var subtitleObserver: NSKeyValueObservation?
private var userObserver: NSKeyValueObservation?
private var distanceObserver: NSKeyValueObservation?
private let subtitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let userLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let distanceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
}
private extension CustomAnnotationView {
func updateAndAddObservers(for customAnnotation: CustomAnnotation) {
subtitleLabel.text = customAnnotation.subtitle
subtitleObserver = customAnnotation.observe(\.subtitle) { [weak self] customAnnotation, _ in
self?.subtitleLabel.text = customAnnotation.subtitle
}
userLabel.text = customAnnotation.userId
userObserver = customAnnotation.observe(\.userId) { [weak self] customAnnotation, _ in
self?.userLabel.text = customAnnotation.userId
}
distanceLabel.text = "\(customAnnotation.distance) meters"
distanceObserver = customAnnotation.observe(\.distance) { [weak self] customAnnotation, _ in
self?.distanceLabel.text = "\(customAnnotation.distance) meters"
}
}
func removeAnyObservers() {
subtitleObserver = nil
userObserver = nil
distanceObserver = nil
}
func createCallOutWithDataFrom(customAnnotation: CustomAnnotation?) -> UIView {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(subtitleLabel)
view.addSubview(userLabel)
view.addSubview(distanceLabel)
NSLayoutConstraint.activate([
subtitleLabel.topAnchor.constraint(equalTo: view.topAnchor),
subtitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
subtitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
subtitleLabel.bottomAnchor.constraint(equalTo: userLabel.topAnchor),
userLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
userLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
userLabel.bottomAnchor.constraint(equalTo: distanceLabel.topAnchor),
distanceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
distanceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
distanceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
if let customAnnotation = customAnnotation {
updateAndAddObservers(for: customAnnotation)
}
return view
}
}
That yields:
Related
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.
I'm struggling with passing data from a map pin to a new vc.
Scenario
Map pin data (name, subtitle & coordinates) are currently being read from a JSON file. Map pins are dropped, when map pin has been clicked a new view will open and display the corresponding title and subtitle and some other data in labels in a new vc.
I've been following this tutorial for extracting data from a JSON file.
Issue: I'm struggling to find a way to pass the data from the map pin to the labels.
I've done a previous project where I pass data from one textfield to a label.
Thank you very much!
EDIT:
I realised I didn't give any code.
newFeatureVC
// Callout accessory
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "POIAnnotations"
if annotation is POIAnnotations {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
let button = UIButton(type: .detailDisclosure)
annotationView!.rightCalloutAccessoryView = button
}
else {
annotationView!.annotation = annotation
}
return annotationView
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "showAnnotationInfo", sender: self)
}
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showAnnotationInfo" {
let destViewController = segue.destination as! ShopDetailViewController
}
}
POIAnnotations
This class is where variables stored for the name, subtitle and coordinates.
class POIAnnotations: NSObject, MKAnnotation {
var title: String?
var subtitle: String?
var coordinate: CLLocationCoordinate2D
init(title:String, subtitle:String, coordinate:CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
super.init()
}
class func fromJSON(_ json: [JSONValue]) -> POIAnnotations? {
// 1
var title: String
if let titleOrNil = json[1].string {
title = titleOrNil
} else {
title = ""
}
var subtitle: String
if let subtitleOrNil = json[2].string {
subtitle = subtitleOrNil
} else {
subtitle = ""
}
// 2
let latitude = (json[3].string! as NSString).doubleValue
let longitude = (json[4].string! as NSString).doubleValue
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
// 3
return POIAnnotations(title: title, subtitle: subtitle, coordinate: coordinate)
}
}
SecondVC
class ShopDetailViewController: UIViewController {
#IBOutlet weak var shopName: UILabel!
#IBOutlet weak var shopRating: UILabel!
#IBOutlet weak var cancelButton: UIBarButtonItem!
var shopNameData = "name"
var shopRatingData = "rating"
What I've already tried doing is the same way that I got the data from the JSON file in the POIAnnotations class.
I tried calling the fromJSON function then tried storing it in variable to display on the label. But I couldn't get my head around how to do it.
Use MKMapViewDelegate method to detect which pin is tapped and then display its data in view controller.
func mapView(MKMapView, didSelect: MKAnnotationView)
The below code works and gives me a draggable annotation view. However, I've noticed that the annotation view is not draggable from the very beginning on, but rather only after the finger has rested for a short moment on the annotation view. When directly going into a drag movement the dragging doesn't affect the annotation view but instead pans the map. It certainly doesn't feel like drag'n'drop. Both on the device and in the simulator.
ViewController (Delegate of the MapView)
override func viewDidLoad() {
/// ...
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(addPin))
mapView.addGestureRecognizer(gestureRecognizer)
}
func addPin(gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer.state != UIGestureRecognizerState.Began {
return
}
for annotation in mapView.annotations {
mapView.removeAnnotation(annotation)
}
let touchLocationInView = gestureRecognizer.locationInView(mapView)
let coordinate = mapView.convertPoint(touchLocationInView, toCoordinateFromView: mapView)
let annotation = DragAnnotation(coordinate: coordinate, title: "draggable", subtitle: "")
mapView.addAnnotation(annotation)
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(DragAnnotation) {
let reuseIdentifier = "DragAnnotationIdentifier"
var annotationView: MKAnnotationView!
if let dequeued = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseIdentifier) {
annotationView = dequeued
} else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
annotationView.annotation = annotation
annotationView.image = UIImage(named: "bluedisk2")
annotationView.canShowCallout = false
annotationView.draggable = true
return annotationView
}
return nil
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
switch (newState) {
case .Starting:
view.dragState = .Dragging
case .Ending, .Canceling:
view.dragState = .None
default: break
}
}
DragAnnotation
import UIKit
import MapKit
class DragAnnotation : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D {
didSet {
print(coordinate)
}
}
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
I don't think you can change the draggable delay, but you could disable it and add your own drag gesture that has no delay (or a shorter delay):
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
var view = mapView.dequeueReusableAnnotationViewWithIdentifier("Foo")
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "Foo")
view?.draggable = false
let drag = UILongPressGestureRecognizer(target: self, action: #selector(handleDrag(_:)))
drag.minimumPressDuration = 0 // set this to whatever you want
drag.allowableMovement = .max
view?.addGestureRecognizer(drag)
} else {
view?.annotation = annotation
}
return view
}
private var startLocation = CGPointZero
func handleDrag(gesture: UILongPressGestureRecognizer) {
let location = gesture.locationInView(mapView)
if gesture.state == .Began {
startLocation = location
} else if gesture.state == .Changed {
gesture.view?.transform = CGAffineTransformMakeTranslation(location.x - startLocation.x, location.y - startLocation.y)
} else if gesture.state == .Ended || gesture.state == .Cancelled {
let annotationView = gesture.view as! MKAnnotationView
let annotation = annotationView.annotation as! DragAnnotation
let translate = CGPoint(x: location.x - startLocation.x, y: location.y - startLocation.y)
let originalLocation = mapView.convertCoordinate(annotation.coordinate, toPointToView: mapView)
let updatedLocation = CGPoint(x: originalLocation.x + translate.x, y: originalLocation.y + translate.y)
annotationView.transform = CGAffineTransformIdentity
annotation.coordinate = mapView.convertPoint(updatedLocation, toCoordinateFromView: mapView)
}
}
By the way, if you're going to change the coordinate of an annotation, you will want to add dynamic keyword to your custom class coordinate declaration so that the appropriate KVO notifications will be issued.
class DragAnnotation: NSObject, MKAnnotation {
dynamic var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String? = nil, subtitle: String? = nil) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
super.init()
}
}
Note, in this example, I set the delay to zero. That means that if you tap on the annotation (the typical UI for "show the callout"), this may prevent that from working correctly. It will treat this as a very short drag. This is why the standard UI requires a delay in the long press before dragging.
So, for this reason, I personally would hesitate to override this behavior shown above because it will be different then standard map behavior that the end user is familiar with. If your map behaves differently than any other maps on the user's device, this could be a source of frustration.
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!
}
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.