Add button to MKPointAnnotation - swift

Having a problem when i try to add a button in a annotation.
Before i've asked this question i've searched for answers on the following pages:
How to add a button to the MKPointAnnotation?
, Adding a button to MKPointAnnotation?
etc.
But all couldn't help me.
This what is tried to do:
var annotation1 = MKPointAnnotation()
annotation1.setCoordinate(locationKamer1)
annotation1.title = "Title1"
annotation1.subtitle = "Subtitle1"
// here i want to add a button which has a segue to another page.
mapView.addAnnotation(annotation1)
Don't know if what i try to do isn't working.
I'm experimenting with swift for the first time.
Hope someone can help me :)
Thanks in advance!

That answer in your first link is basically right, though it needs to be updated for Swift 2.
Bottom line, in answer to your question, you don't add the button when you create the annotation. You create the button when you create its annotation view in viewForAnnotation.
So, you should:
Set the view controller to be the delegate of the map view.
Make the view controller conform to the map view delegate protocol, e.g.:
class ViewController: UIViewController, MKMapViewDelegate { ... }
Add a segue from the view controller (not the button) to the next scene by control dragging from the view controller icon above the scene with the map view to the next scene:
Then select that segue and then give it a storyboard identifier ("NextScene" in my example, though you should use a more descriptive name):
Implement viewForAnnotation to add button as right accessory.
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
var view = mapView.dequeueReusableAnnotationViewWithIdentifier(annotationIdentifier)
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
view?.canShowCallout = true
view?.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
} else {
view?.annotation = annotation
}
return view
}
Implement calloutAccessoryControlTapped which (a) captures which annotation was tapped; and (b) initiates the segue:
var selectedAnnotation: MKPointAnnotation!
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
selectedAnnotation = view.annotation as? MKPointAnnotation
performSegueWithIdentifier("NextScene", sender: self)
}
}
Implement a prepareForSegue that will pass the necessary information (presumably you want to pass the annotation and therefore have a annotation property in that second view controller).
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? SecondViewController {
destination.annotation = selectedAnnotation
}
}
Now you can create your annotation like you were before:
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "Title1"
annotation.subtitle = "Subtitle1"
mapView.addAnnotation(annotation)

Related

Passing Data over Segue From a mapView MKAnnotiation that has been tapped

I am trying to pass data over a segue created by tapping an MkAnnotation callout using the calloutAccessoryControlTapped control. I want to go to the EventViewController and make a screen with more information about the MkAnnotation that was selected.
I've tried a number of difference methods including creating a custom class and trying to send that. The segue happens correctly but no data is passed to my second UIViewController (eventViewController).
I assume I am doing the wrong thing every time, but am finding it hard to debug the code due to the point at which I am assigning the data to the variable is also the trigger for the segue.
i.e. I am assuming the data isn't getting assigned at all, but the "selectedAnnotation" variable is getting passed across correctly but obviously its hard to tell.
override func prepare(for segue: UIStoryboardSegue, sender: (Any)?) {
if segue.identifier == "goToEventScreen" {
selectedAnnotation = mapView.selectedAnnotations.lastObject as? MKAnnotation
let destinationVC = segue.destination as! EventViewController
destinationVC.points = selectedAnnotation
}
}
}
'''
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl){
performSegue(withIdentifier: "goToEventScreen", sender: self)
}
}
Thanks in advance.
You need to set your annotationView in the delegate method as the sender of performSegue method.
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
performSegue(withIdentifier: "goToEventScreen", sender: view)
}
}
Then in prepare for segue method:
override func prepare(for segue: UIStoryboardSegue, sender: (Any)?) {
if segue.identifier == "goToEventScreen" {
if let annotationView = sender as? MKAnnotationView {
selectedAnnotation = annotationView.annotation
let destinationVC = segue.destination as! EventViewController
destinationVC.points = selectedAnnotation
}
}
}
What you do is getting the annotation from the selected annotationView and assigning it to the destinationVC's points.

Swift distinguishing between callout buttons

Hi I'm mostly new to coding and this is my first project that I've worked on. I'm making an app that displays pools which are hiring around your address and wanted to make it so that pressing on the callouts will bring you to a unique ViewController depending on which one you pressed.
From what I've seen so far online it looks like it needs to be done in an extension of the ViewController but was stuck on how to differentiate between callouts (as of right now I have it so that pressing it will show the directions, but I'd rather show more information that just that, which to my limited knowledge would mean displaying another view controller unless anyone else has any other ideas)
extension MapViewController : MKMapViewDelegate
{
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if let annotation = annotation as? Pools {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView =
mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView{
dequeuedView.annotation = annotation
view = dequeuedView
}
else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
}
return view
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let location = view.annotation as! Pools
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
}
Any help would be greatly appreciated!

Swift Parse MapView Not Displaying CalloutAccessory and Segueing

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. I think it may have to do with the fact that in the new XCode update my calloutaccessory hasn't been showing. Here's a screenshot from my app. MapView Annotation
How about using didSelect method instead of calloutAccessoryControlTapped?
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
performSegue(withIdentifier: "goToUserProfile", sender: view)
print(view.annotation?.title) // annotation's title
print(view.annotation?.subtitle) // annotation's subttitle
}

Swift MapKit Centers on Bottom vs. Top of Pin

I'm using the same function call to display a pin & annotation on my map. When I use this on a current location inside of a reverse geocode call, the pin centers on the base of the pin (obscuring the top of the callout). When I call the same code in any other circumstance (such as when using coordinates passed into the view controller), the map centers on the top of the pin (which is what I want). Code below, image just after this. placesData is an object of a custom class PlacesData, that conforms to both: NSObject, MKAnnotation. Thanks in advance!
func updateMap() {
centerMap(mapLocation: (placeData?.coordinate)!, regionRadius: regionRadius)
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotation(self.placeData!)
mapView.selectAnnotation(self.placeData!, animated: true)
}
Also: if significant, here is the code for my function: mapView(_:viewFor:)
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifer = "Marker"
var view: MKPinAnnotationView
if let dequedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifer) as? MKPinAnnotationView {
dequedView.annotation = annotation
view = dequedView
} else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifer)
view.canShowCallout = true
view.rightCalloutAccessoryView = UIButton(type: .custom)
}
return view
}

Selecting the object at index in a MapView in SWIFT

I am trying to present another view controller once the right callout button is pressed but it must go to the correct indexPath, similar to how this happens in a table view. this is where I am up to:
I have created a custom annotation like so:
class annotationCustom: NSObject, MKAnnotation {
var coordinate : CLLocationCoordinate2D
var title : NSString!
var subtitle : NSString!
var isUserAnnotation : Bool
var dataImage = [NSData]()
init(coordinate: CLLocationCoordinate2D, title: NSString!, subtitle: NSString!){
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.isUserAnnotation = false
}
}
and then set the viewForAnnotation like so:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
var dqPin = "pin"
var view = mapView.dequeueReusableAnnotationViewWithIdentifier(dqPin) as? MKPinAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: dqPin)
view!.canShowCallout = true
var arrowBut = UIImage(named: "arrow")
var arrowButton = UIButton()
arrowButton.setImage(arrowBut, forState: UIControlState.Normal)
arrowButton.tintColor = UIColor.blackColor()
arrowButton.frame = CGRectMake(0, 0, 30, 30)
view!.rightCalloutAccessoryView = arrowButton
}
return view
}
Now I know I have to use the function:
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView {
let sb = UIStoryboard(name: "Main", bundle: nil)
let userProfileVC = sb.instantiateViewControllerWithIdentifier("usersProfileVC") as UsersProfilesViewController
userProfileVC.profileName = annotationCustom.title // error: 'annotationCustom.Type' does not have a member named 'title'
performSegueWithIdentifier("goToUserFromMap", sender: self)
}
}
}
Can somebody fill in the gaps or help me out please.
Thanks
Instead of:
if control == annotationView.rightCalloutAccessoryView {
it should be:
if control == view.rightCalloutAccessoryView {
view is the internal, private name of the parameter and annotationView is the external name visible to the caller.
However, in this case, you don't really need to check control in the first place since your callouts have only a single control (rightCalloutAccessoryView). They don't have a leftCalloutAccessoryView so there's only one control the user could tap.
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
println("disclosure pressed on: \(view.annotation.title)")
}
By the way, is the code shown in viewForAnnotation the actual code? Because it looks like the result of the dequeueReusableAnnotationViewWithIdentifier is ignored and there is a mysterious extra closing } before the return view.
Here:
userProfileVC.profileName = annotationCustom.title
annotationCustom is the name of your class -- not the instance of it (also, convention is to start class names with a capital letter). view.annotation is the instance here. Cast it as annotationCustom to access any custom properties (though title isn't custom):
if let ac = view.annotation as? annotationCustom {
userProfileVC.profileName = ac.title
}