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
}
}
Related
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
I've researched on this topic for almost 3 days straight trying to figure out how to present my detail view controller by clicking on the info button of a map annotation in my map view controller. Basically, I can get the annotation to show, but when I click the annotation, nothing happens. I would like to have it present my detail view controller, the same way that when that same item is clicked on in my table view controller, it goes directly to its respective detail view.
Any help would be much, much appreciated!
This is the image of the map annotation I currently have
Below is my code for my MapViewController. I feel like there is something wrong or something is missing in either my prepareForSegue or annotationView functions.
extension MapViewController {
// 1
#objc(mapView:viewForAnnotation:) func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? Annotations {
let reuseID = "pin"
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseID)
as? MKPinAnnotationView { // 2
dequeuedView.annotation = annotation
view = dequeuedView
} else {
// 3
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseID)
view.canShowCallout = true
view.isUserInteractionEnabled = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton.init(type: .detailDisclosure) as UIButton
}
return view
}
return nil
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView {
print(view.annotation?.title)
performSegue(withIdentifier: "moreDetail", sender: self)
}
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "moreDetail") {
// pass data to next view
let destViewController:PancakeHouseViewController = segue.destination as! PancakeHouseViewController
destViewController.viaSegue = sender as! MKAnnotationView
}
}
}
I have also included a variable inside my detail view controller (PancakeHouseViewController)... I don't know if it's supposed to be there or not.
var viaSegue = MKAnnotationView()
I believe the problem may just be in the method signature for calloutAccessoryControlTapped. It should be:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
I am rather new to swift and object oriented programming and I am trying to switch views from a view that contains a map to another view by tapping on a right accessory callout button in the annotation of a pin that is dropped by a long press. In the storyboard, I have created a segue between the two views and assigned the segue with the identifier mapseg. Unfortunately, after trying everything I could find via google I cannot get the segue to occur when I tap the right accessory callout button and have no idea why.The application itself is a tabbed application with three tabs. The view for the second tab is the one that contains the map. Also, I don't know if this could have something to do with it, but the view I am trying to transition from is embedded in a navigation controller. Here is my code for the view that I am trying to transition from.
import UIKit
import MapKit
class SecondViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var MapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.MapView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseID = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseID)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.enabled=true
let btn = UIButton(type: .DetailDisclosure)
btn.titleForState(UIControlState.Normal)
pinView!.rightCalloutAccessoryView = btn as UIView
return pinView
}
#IBAction func dropPin(sender: UILongPressGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began {
let location = sender.locationInView(self.MapView)
let locCoord = self.MapView.convertPoint(location, toCoordinateFromView: self.MapView)
let annotation = MKPointAnnotation()
annotation.coordinate = locCoord
annotation.title = "City Name"
annotation.subtitle = ""
self.MapView.removeAnnotations(MapView.annotations)
self.MapView.addAnnotation(annotation)
self.MapView.selectAnnotation(annotation, animated: true)
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
self.performSegueWithIdentifier("mapseg", sender: self)
}
}
}
}
I think you need to override prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) method. And check whether if working
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
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!)")
}