Get MKPointAnnotation title - ios

I've created a MKMapView which contains several MKPointAnnotations on the map. When the user clicks the UIButton in the view I would like to print the title in the log. How can I do that? So far I have this:
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 = .Purple
var rightButton: AnyObject! = UIButton.buttonWithType(UIButtonType.DetailDisclosure)
//MapPointAnnotation *point = (MapPointAnnotation*)pinView.annotation;
//rightButton.venue = point.venue;
rightButton.titleForState(UIControlState.Normal)
rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
pinView!.rightCalloutAccessoryView = rightButton as UIView
}
else {
pinView!.annotation = annotation
}
return pinView
}
func rightButtonTapped(sender: AnyObject) {
}

In the custom rightButtonTapped method, an easy and reliable way to get a reference to the annotation that was tapped is to use the map view's selectedAnnotations array:
func rightButtonTapped(sender: AnyObject) {
if self.mapView.selectedAnnotations?.count == 0 {
//no annotation selected
return;
}
if let ann = self.mapView.selectedAnnotations[0] as? MKAnnotation {
println("\(ann.title!)")
}
}
(Even though selectedAnnotations is an array, the map view only allows one annotation to be "selected" at a time so the currently selected annotation is always at index 0.)
However, a better way than using a custom button method is to use the map view's calloutAccessoryControlTapped delegate method. The delegate method passes you a reference to the annotation view that was tapped from which you can easily get the underlying annotation.
To use the delegate method, remove the addTarget line for your custom method:
//Do NOT call addTarget if you want to use the calloutAccessoryControlTapped
//delegate method instead of a custom button method.
//rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
and then implement the delegate method instead of your custom button method:
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
println("\(view.annotation.title!)")
}

Related

Swift - How can I trigger a segue when MKAnnotation button is pressed?

Swift-Newbie question:
I have two view controllers: My home VC which uses MapKit and a second 'MapDetailViewController' to display additional information.
On the home VC, I have added a point of interest with an annotation to display the name of the point and subtitle when the user clicks on the pin. How can I segue to my MapDetailViewController when the user clicks on the map annotation?
// Map view delegate which handles the annotation view
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation:
MKAnnotation) ->
MKAnnotationView? {
guard let annotation = annotation as? Artwork else { return nil }
let identifier = "marker"
var view: MKMarkerAnnotationView
if let dequeuedView =
mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as?
MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKMarkerAnnotationView(annotation: annotation,
reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
// Broke up the button initialisation so that it has unique id 'button'
let button = UIButton(type: .detailDisclosure)
view.rightCalloutAccessoryView = button
// Make the segue happen - tried to make it happen when object 'button' is pressed
performSegue(withIdentifier: "MapDetail", sender: button)
}
return view
}
This is an extract from an old project I was working on. It opens a new tableview controller when the information accessory is tapped on the annotation. I had a made a custom class called Annotation which held all the information about the annotation. Im not sure if this will help :)
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl) {
let annotation = view.annotation as! Annotation
let event = annotation.event
if control == view.rightCalloutAccessoryView {
let destination = self.storyboard!.instantiateViewController(withIdentifier: "EventViewController") as! EventViewController
self.navigationController!.pushViewController(destination, animated: true)
destination.event = event
}
}

Swift Parse Segueing To Another View Controller When MapView Annotation Is Clicked

I currently have a UIMapView with different annotations pinned for the different locations of users on the map. I'm trying to segue to the respective users profile when the annotation is clicked. Here's my code to set up the annotations:
let query = PFQuery(className: "location")
query.findObjectsInBackground { (objects, error) in
if let brandsLocation = objects {
for object in brandsLocation {
if let brandLocation = object as? PFObject {
let annotation = MKPointAnnotation()
let annotationPoint = object["geoPoint"] as! PFGeoPoint
annotation.coordinate = CLLocationCoordinate2DMake(annotationPoint.latitude, annotationPoint.longitude)
annotation.title = brandLocation["annotationTitle"] as? String
annotation.subtitle = brandLocation["annotationSubtitle"] as? String
self.map.addAnnotation(annotation)
}
}
}
}
This is where I believe the segue is supposed to happen, however it isn't doing anything for some reason.
// When user taps on the disclosure button you can perform a segue
to navigate to another view controller
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView{
performSegue(withIdentifier: "goToUserProfile", sender: view)
print(view.annotation?.title) // annotation's title
print(view.annotation?.subtitle) // annotation's subttitle
//Perform a segue here to navigate to another viewcontroller
// On tapping the disclosure button you will get here
}
}
// Here we add disclosure button inside annotation window
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
print("viewForannotation")
if annotation is MKUserLocation {
//return nil
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
//println("Pinview was nil")
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
}
let button = UIButton(type: UIButtonType.detailDisclosure) as UIButton // button with info sign in it
pinView?.rightCalloutAccessoryView = button
return pinView
}
I'd like for the segue to happen when the annotation is clicked or double clicked, and then perform a segue to that new view controller while sending the specific username info as well. I'm pretty sure I'll be able to send the user info but I just need to figure out how to make the segue happen. Any help is much appreciated! Thank you
UPDATE The problem may be that my CalloutAccessory isn't displaying. Check out this screenshot from when I click the pin in my app:
MapView Annotation Clicked

Detect on calloutAccessoryControlTapped only the tap on rightCalloutAccessoryView

My calloutAccessoryControlTapped is also called when I just tap on annotation view and this behavior it's right. But how can I detect if the user has tapped on the right accessory view (in my case a detail disclosure button) and not just in the view?
I added a simple check but it doesn't work.
import UIKit
import MapKit
extension MapVC: MKMapViewDelegate, CLLocationManagerDelegate
{
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
{
if control == view.rightCalloutAccessoryView
{
... // enter here even if I tapped on the view annotation and not on button
}
}
}
To achieve it you would need to add target for the right accessory view. You can achieve it by setting button to rightCalloutAccessoryView as shown in the code snippet.
class MapViewController: UIViewController, MKMapViewDelegate {
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is Annotation {
let annotationView = AnnotationView(annotation: annotation, reuseIdentifier: "reuseIdentifier")
let rightButton = UIButton(type: .DetailDisclosure)
rightButton.addTarget(self, action: #selector(didClickDetailDisclosure(_:)), forControlEvents: .TouchUpInside)
annotationView.rightCalloutAccessoryView = rightButton
}
return nil
}
func didClickDetailDisclosure(button: UIButton) {
// TODO: Perform action when was clicked on right callout accessory view.
}
}
// Helper classes.
class Annotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
class AnnotationView: MKAnnotationView {
}
Using UIView and UITapGestureRecognizer instead of UIControl
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "reuseIdentifier")
let gestureView = UIView(frame:CGRect(x: 0,y: 0,width: 20,height: 20))
let gestureRecognizer = UITapGestureRecognizer()
gestureRecognizer.addTarget(self, action: #selector(MapViewController.didClickGestureRecognizer(_:)))
gestureView.addGestureRecognizer(gestureRecognizer)
gestureView.backgroundColor = UIColor.redColor()
annotationView.rightCalloutAccessoryView = gestureView
return annotationView
}
func didClickGestureRecognizer(sender:UITapGestureRecognizer) -> Void {
print("didClickGestureRecognizer")
}
When you click on rightCalloutAccessoryView, only didClickGestureRecognizer will be called,but your calloutAccessoryControlTapped cannot be invoked anyone.
2.If you have a UIControl as rightCalloutAccessoryView,you can tap directly on MKAnnotationView.Otherwise MKAnnotationView cannot be tapped.
Both your selector and calloutAccessoryControlTapped will be called when you tap rightCalloutAccessoryView or tap directly on MKAnnotationView
3.If you have a UIControl as leftCalloutAccessoryView,both your selector and calloutAccessoryControlTapped will be called when you tapped on it.
4.Since iOS 9,you can has a detailCalloutAccessoryView in your MKAnnotationView.Only your selector will be called when you tapped on it.
5.Also you can create your own custom MKAnnotationView,and change its behaviors.

Swift - passing data from mapkit annotation to another view by disclosure button

I have a map with annotations, after clicking the pin, the callout shows with title of annotation and a disclosure button. When I tap button the segue is triggered and I move to another view. How to determine what annotation was clicked, or pass the title to another view.
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)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.pinColor = .Red
var calloutButton = UIButton.buttonWithType(.DetailDisclosure) as UIButton
pinView!.rightCalloutAccessoryView = calloutButton
} else {
pinView!.annotation = annotation
}
return pinView!
}
func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == annotationView.rightCalloutAccessoryView {
performSegueWithIdentifier("Detail", sender: self)
}
}
Thanks
You can use MKAnnotation title of annotation property for finding the pins if annotation title is different
annotationView.annotation.title
return String.Compare String to differenciate.
or
Use tag property in pinview
pinView!.tag //set tag for each pin in `viewForAnnotation` method
In
func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
//get tag here
if(annotationView.tag == 0){
//Do for 0 pin
}
if control == annotationView.rightCalloutAccessoryView {
performSegueWithIdentifier("Detail", sender: self)
}
}
Try saving the title to you the NSUserDefaults then grabbing that object in the new view. This is the method I use.
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView{
println(view.annotation.title) // annotation's title
let title = view.annotation.title
NSUserDefaults.standardUserDefaults().setObject(title, forKey: "Title")
var InfoView = self.storyboard?.instantiateViewControllerWithIdentifier("Spot") as! UIViewController
Once you have saved the title to the NSUserDefaults you can simply grab the object in the new "InfoView or whatever you are calling it" like this let spotTitle = NSUserDefaults.standardUserDefaults().objectForKey("SpotTitle") as! String
Hope this helps

Button inside custom Callout View not responding (Swift)

I have a custom MKPinAnnotationView created as so:
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 = false
pinView!.animatesDrop = true
pinView!.pinColor = .Red
pinView!.draggable = true;
} else {
pinView!.annotation = annotation
}
return pinView
}
Then I add a subview to it later with a button like this:
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
view.addSubview(confirm)
}
The problem is that the button inside the subview is not responsive at all. What could be causing that? I already tried playing around with userInteraction enabled.
Thanks
Not sure if this fits or helps but in my case I where I have similar code I set
pinView!.canShowCallout = true
and then if you want a simple way to catch the button clicks I put a func inside of my ViewContriller:
func buttonClicked (sender : UIButton!) {
println("Button Clicked")
}
It sounds like you want to do something a bit different though.
Opps.. in previous answer I forgot that I added the button just above the
return pinView
....
let button : UIButton = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as UIButton
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
pinView!.rightCalloutAccessoryView = button
return pinView
Now the buttonClicked func can catch the callout.

Resources