passing data to another view controller when tapping on annotation - ios

I have an app that displays locations on map. I want to pass the data from the 1st view (MapViewController) to the another view (locationDetailViewController)
I have used the code below to pass the data and it takes me to another second view. however, it doesn't pass the mutable array to another view controller...
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{PlaceDetailViewController *det=[[PlaceDetailViewController alloc]init];
det.PlaceDetailMutableArray=PlaceMutableArray;
[self performSegueWithIdentifier:#"DetailView" sender:view];}
thanks in advance

Try this:
On your SecondViewController.m, add the method (Don't forget to declare it on your SecondViewController.h too):
-(void)setValue:(NSMutableArray*)array
{
NSMutableArray *PlaceDetailMutableArray = [[NSMutableArray alloc] init];
PlaceDetailMutableArray = array;
}
And on the first ViewController, add the code below:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"DetailView"]) {
PlaceDetailViewController *det= segue.destinationViewController;
[det setValue: PlaceMutableArray];
}
}
Finally, change the code of your example for:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:#"DetailView" sender:view];
}

Related

Detect what route the user tap on map

I have a project where I show directions between current location and another location on a map (MapKit)
All works well. And I can get alternative routes.
request.requestsAlternateRoutes = YES;
But when the user tap on a route I show an annotation with distance and some other info. I want to pass this spesific route to another view. How can I achieve that? Like the original Map app on iOS. I can get different routes, and tap on a route to get direction details.
I have googled a lot, and the closest example is this:
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
// Now handle the result
if (error) {
NSLog(#"There was an error getting your directions");
return;
}
_currentRoute = [response.routes firstObject];
But _currentRoute is the first one. I want to let the user select currentRoute on tap on the map.
I figured it out. Not so elegant, but it does the job.
Make an ivar - NSMutableArray *routeArray in the header file.
I also have a MKMapView outlet in my header.
In my getDirections method, I tagged the MKMapView instance with a counter and added the route object to the routeArray.
When the delegate for making the Annotation is run, I can tag the anntotationView with the mapView.tag.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
When I tap on the annotation:
(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
I pass the view.tag to my segue.
NSLog(#"Counter tag: %ld", (long)view.tag);
[self performSegueWithIdentifier:#"detaljer" sender:view];
I can then pass my selected route to my new view.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
WeatherTableViewController *routeController =
[segue destinationViewController];
long row = [sender tag];
MKRoute *route = routeArray[row];
routeController.selectedRoute = route;
}

Pass data between view controller [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 7 years ago.
I'm trying to pass an object (PFObject) from an view controller to another,
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
RestauCardViewController *restauCard = [[RestauCardViewController alloc]init];
RestaurantAnnotation *restoAnnotation = (RestaurantAnnotation *)view.annotation;
restauCard.restaurant = restoAnnotation.restaurant;
[self performSegueWithIdentifier:#"segueToCard" sender:nil];
}
When I try to display the object in the other view controller I got null:
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface RestauCardViewController : UIViewController
#property(nonatomic) PFObject *restaurant;
#end
This is my viewDidLoad function
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"The restaurant name is : %#",_restaurant[#"nom"]);
}
You need to use -prepareForSegue to manage this situation, and you'll need an iVar to keep the restaurant name.
So in the top of your map's .m file, add an ivar NSString
#implementation yourViewController{
NSString *sRestName; //This is empty until the user selects a restaurant
}
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
sRestName = //Set the name of your restaurent here, it's just a string.
//You could set any other type of object (a restaurent object or a PFOjbect or anything,
//just change the ivar accordingly
[self performSegueWithIdentifier:#"segueToCard" sender:nil];
}
What you're going to do is replace your old code with the code just above, you just need to perform the segue to call the following method.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"fromHomeToList"]){
RestauCardViewController *vc = (RestauCardViewController*)segue.destinationViewController;
vc.restaurant = sRestName; //here you're just giving the property of the new controller the content of your ivar.
}
That way you can pass an object from your map tap to your next controller. You're also sure it will never be nil because the user tapped it ; if it was nil, well, he couldn't have tapped it in the first place !
Note that I assumed you were using a string for your restaurant name, but if you change the ivar on the top, you can use anything you want, as long as you can retrieve it from tapping on the map. If you can't, I need more details to walk you through another solution.
Ask me if you have any quesiton, otherwise this should work !
You have to set the restaurent inside the UIViewController method "prepareSegue...". It's called after you performeSegueWithIdentifier, so the destination controller is accessible and you can test the segue.identifier and set the restaurent to the controller.
Example :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueRestaurentDetails"]) {
RestauCardViewController *destController = (RestaurentDetailsViewController *)segue.destinationViewController;
destController.restaurant = (RestaurantAnnotation *)view.annotation;
}
Implement prepareForSegue:sender: method in ViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"segueToCard"]) {
RestauCardViewController *controller = (RestauCardViewController *)segue.destinationViewController;
controller.restaurant = (RestaurantAnnotation *)view.annotation;
}
}

xcode - UIButton in mapView annotations w/ tags?

I've read most of the other questions here, as well as read a lot, but I'm not finding an answer.
I'm using this code to create the buttons
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
NSLog(#"called the MKAnnotView method, tag is: %i", self.tag);
static NSString *s = #"ann";
MKAnnotationView *pin = [mapView dequeueReusableAnnotationViewWithIdentifier:s];
if (!pin) {
pin = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:s];
pin.canShowCallout = YES;
pin.calloutOffset = CGPointMake(0, 0);
UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
button.tag=self.tag++;
[button addTarget:self
action:#selector(viewDetails:)
forControlEvents:UIControlEventTouchUpInside];
pin.rightCalloutAccessoryView = button;
}
return pin;
}
-(void) viewDetails: (id) sender {
UIButton *button = sender;
NSLog(#"viewDetails called with button.tag: %i",button.tag);
//[self performSegueWithIdentifier:#"detailView" sender:self];
}
I'm creating a variable amount of pins, in a specific order. When I create them, I get the expected output of "called the MKAnnotView method, tag is 0" then 1, 2, 3, etc.
The annotations are in a very specific order. Zeroth, first, second, so forth. I expect the buttons I created for each annotation to have a tag that fits their index, so that when I segue to the detailViewController, I know which item on the list needs to be pulled and the view populated.
That's not happening. The buttons are each getting randomly assigned tags that repeat. My last case had two tags with 3, and two with zero.
I cannot for the life of me understand why.
Any hints?
Based on comments to my question, the solution I came up with was implementing the following
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
self.tag = [self.annotArray indexOfObject:view.annotation];
NSLog(#"calloutAccessoryControlTapped: annotation = %i", self.tag);
[self performSegueWithIdentifier:#"ShowDetails" sender:self];
}
Whenever I create an annotation, I was already adding it to an annotArray property of this viewController for another use (as the view's built-in annotation array was not populating in a predictable manner)
my prepareForSegue method pulls self.tag and updates a property of the destinationViewController.

iOS: How to load an object in a mapview depending on which annotation was tapped?

I have an app with a mapview containing various building objects (my own class). Each building has an annotation property which all works great and the user can search a table for the desired building, tap a button and it'll show the relevant annotation.
Now I have a callout button on the annotation which calls a method (showDetails) that pushes a segue to the DetailViewController, but the issue I'm having is getting the right building across to this DetailViewController. All the buildings are stored in an array in the data controller and I'd like to load one from that if possible.
So far I've used the controlWasTapped method where I've simply put:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
for (Building* theBuilding in self.datacontroller.masterArray){
if(theBuilding.name isEqualToString:view.annotation.title){
NSLog(#"theBuilding's name %#", theBuilding.name);
}
}
This works fine and returns the correct building but I'm stumped as to how to get this to the DetailViewController. I have a PrepareForSegue method but how would I get the correct building to it? Also, now that I have DidSelectAnnotation, is there any need for my showDetails method?
I was thinking I could modify the showDetails method so that it took an argument of Building type and then I could provide the Building details in prepareForSegue but if there is a much better way I'd love to know.
So, what's the best plan? Many thanks
Declare an instance variable in the class that contains the code you posted.
Building *buildingOfInterest;
then
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
for (Building* theBuilding in self.datacontroller.masterArray)
if(theBuilding.name isEqualToString:view.annotation.title) {
buildingOfInterest = theBuilding;
[self performSegueWithIdentifier:#"ShowBuildingSegue" sender:self];
break;
}
}
then:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowBuildingSegue"])
((MyBuildingVC *)segue.destinationViewController).building = buildingOfInterest;
}
and in your target view controller's header file:
#interface MyBuildingVC : UIViewController
#property (strong) Building * building;
#end

MkMapView annotation selection dilemma?

Ok, so I have a map view that has a bunch of annotations on it. Certain annotations when selected need to display extended info in a small table view which i am doing by resizing the mapview to half screen and animating into view a table in the bottom half. If another annotation is selected that doesn't need the extra info then in the didDeselectAnnotationView: method i hide the table and go back to the full map view, rinse and repeat.. So far so good, everything is working great.
The issue i am having though is that if a user selects another annotation while they currently have an annotation selected then didSelectAnnotationView delegate method gets called BEFORE the didDeselectAnnotationView.
This is obviously a problem because i am using these two methods to decide whether or not i need to display/hide the info table below the mapview, see code below:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if ([view.annotation isKindOfClass:[MapLocation class]])
{
if ([self.selectedAnnotation numberOfEvents] == 1)
{
mapTableViewIsVisible = NO;
}
else if ([self.selectedAnnotation numberOfEvents] > 1)
{
// launch mini tableview
mapTableViewIsVisible = YES;
}
[self loadMapTableViewWithEvents:self.selectedAnnotation.events
forAnnotation:self.selectedAnnotation];
}
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
if ([view.annotation isKindOfClass:[MapLocation class]])
{
mapTableViewIsVisible = NO;
[self loadMapTableViewWithEvents:nil forAnnotation:(MapLocation*)view.annotation];
}
}
So for example if i select an annotation that needs the maptable and i currently have a regular annotation selected then the mapTable is loaded when the didSelectAnnotationView method above is called, however it is immediately hidden again because the didDeselectAnnotationView is called right after.
So far i havent been able to figure out a way to fix this.
Any ideas??
You could check for the case where no annotations are visible in didDeselectAnnotationView and then clean up your tableview on this case only. As all other cases will be handled by didSelectAnnotation view.
Something like:
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{
if([[mapView selectedAnnotations] count]==0){
mapTableViewIsVisible = NO;
[self loadMapTableViewWithEvents:nil forAnnotation:(MapLocation*)view.annotation];
}
}

Resources