I have a map that has multiple annotations that are plotted on the map sourced from JSON data that are broken up by category within a UITableViewController that contains information like their latitude and longitude as well as other information like: address, phone number and so on...
I know how to add a detail disclosure button to the annotation view for each one, but how to do I get that information into a detail viewController?
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annoPin"];
MKAnnotationView *view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:#"annoView"];
if(!view) {
view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annoView"];
}
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:nil action:#selector(showDetails :) forControlEvents:UIControlEventTouchUpInside];
pinView.animatesDrop = YES;
view.rightCalloutAccessoryView = rightButton;
view.image = [UIImage imageNamed:#"check.png"];
view.enabled = YES;
view.canShowCallout = YES;
return view;
}
As you can see I've created a button, but how do I get the JSON info for each category over to a detail ViewController for each category inside of the UITableViewController?
When you create the annotation that you add to your mapview you need to give it all the information that will eventually get to the detail view controller, or at least a reference to the data that you can get later. Then in mapView:annotationView:calloutAccessoryControlTapped:, which gets called on the map delegate when a disclosure button is tapped, you get the annotationview's annotation and get the data from there.
Related
I have a code for creating annotation view :
- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id<MKAnnotation>)myannotation{
MKAnnotationView *view = nil;
view = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"identifier"];
if([myannotation isKindOfClass:[myAnnotation class]]){
if(nil == view) {
view = [[MKPinAnnotationView alloc] initWithAnnotation:myannotation
reuseIdentifier:#"identifier"];
view.canShowCallout = YES;
}
myAnnotation *anns= (myAnnotation*)myannotation;
_annimge=anns.Img;
UIButton *btnViewVenue = [UIButton buttonWithType:UIButtonTypeContactAdd];
[btnViewVenue addTarget:self action:#selector(ButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
btnViewVenue.titleLabel.text=#"GĂ“WNOKURWA";
view.rightCalloutAccessoryView=btnViewVenue;
}
return view;
}
I would like to show image on button press, thats why I signing image property of annotation to _annimge, but it only shows the last image added, no matter which annotation is active, same thing was happening with ID property which I added, it only returned the highest ID (last added).I am new at iOS and trying to fix this issue for way too long. Thanks for help.
I am trying to use a custom image on my MKAnnotationView when I use the following code I get no image on my annotation. I have checked in debug to ensure the image is being properly loaded into the UIImage.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"String"];
if(!annotationView) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"String"];
UIButton *directionButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *directionIcon = [UIImage imageNamed:#"IconDirections"];
[directionButton setImage:directionIcon forState:UIControlStateNormal];
annotationView.rightCalloutAccessoryView = directionButton;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
There are two main issues:
The frame of the custom callout button is not set making it essentially invisible.
An MKAnnotationView is being created but its image property (the image of the annotation itself -- not the callout button's) is not set. This makes the whole annotation invisible.
For issue 1, set the button's frame to some appropriate value. For example:
UIImage *directionIcon = [UIImage imageNamed:#"IconDirections"];
directionButton.frame =
CGRectMake(0, 0, directionIcon.size.width, directionIcon.size.height);
For issue 2, set the annotation view's image (or create an MKPinAnnotationView instead):
annotationView.image = [UIImage imageNamed:#"SomeIcon"];
Additionally, you should handle view re-use correctly by updating the annotation property.
Complete example:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"String"];
if(!annotationView) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"String"];
annotationView.image = [UIImage imageNamed:#"SomeIcon"];
UIButton *directionButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *directionIcon = [UIImage imageNamed:#"IconDirections"];
directionButton.frame =
CGRectMake(0, 0, directionIcon.size.width, directionIcon.size.height);
[directionButton setImage:directionIcon forState:UIControlStateNormal];
annotationView.rightCalloutAccessoryView = directionButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
}
else {
//update annotation to current if re-using a view
annotationView.annotation = annotation;
}
return annotationView;
}
In order for the callout to be shown, the annotation must be selected. To do this programmatically, call:
[mapView selectAnnotation:annotation animated:YES];
where annotation is the specific MKAnnotation for which you want a callout displayed.
You'll almost certainly want to put this in - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views.
There are a few caveats to consider, so here are two other posts that have some great answers and relevant discussions:
How to trigger MKAnnotationView's callout view without touching the pin?
Wanted: How to reliably, consistently select an MKMapView annotation
I am trying to show annotations on a mapView. All annotations come from JSON objects. They are divided into three groups. The user can select which annotations should be shown selecting an option on an segmentedIndex control.
As for now, the app is working as expected, the user selects an option from the segmentedIndex control, and the annotations are shown on the mapView.
My current issue is that I need the user to click on the callout view to open another viewController.
I think my code is right, but I guess it isn't then the showed callout view is the default calloutview, with title and subtitle. No action is fired when clicked on it.
Any help is welcome.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[PlaceMark class]]) {
MKPinAnnotationView *annotationView =
(MKPinAnnotationView *)[myMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation
reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
// Create a UIButton object to add on the
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[annotationView setRightCalloutAccessoryView:rightButton];
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[leftButton setTitle:annotation.title forState:UIControlStateNormal];
[annotationView setLeftCalloutAccessoryView:leftButton];
return annotationView;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
if ([(UIButton*)control buttonType] == UIButtonTypeDetailDisclosure){
// Do your thing when the detailDisclosureButton is touched
UIViewController *mapDetailViewController = [[UIViewController alloc] init];
[[self navigationController] pushViewController:mapDetailViewController animated:YES];
} else if([(UIButton*)control buttonType] == UIButtonTypeInfoDark) {
// Do your thing when the infoDarkButton is touched
NSLog(#"infoDarkButton for longitude: %f and latitude: %f",
[(PlaceMark*)[view annotation] coordinate].longitude,
[(PlaceMark*)[view annotation] coordinate].latitude);
}
}
Most likely the map view's delegate is not set in which case it won't call viewForAnnotation and will instead create a default view (red pin with a callout showing only the title and subtitle -- no buttons).
The declaration in the header file does not set the map view's delegate. That just tells the compiler that this class intends to implement certain delegate methods.
In the xib/storyboard, right-click on the map view and connect the delegate outlet to the view controller or, in viewDidLoad, put mapView.delegate = self;.
Unrelated, but I want to point out that in calloutAccessoryControlTapped, rather than checking the buttonType, you probably want to just know whether it's the right or left button so just do:
if (control == view.rightCalloutAccessoryView) ...
See https://stackoverflow.com/a/9113611/467105 for a complete example.
There are at least two problems with checking the buttonType:
What if you want to use the same type for both buttons (eg. Custom)?
In iOS 7, setting a button to UIButtonTypeDetailDisclosure ends up actually creating a button of type Info (see MKAnnotationView always shows infoButton instead of detailDisclosure btn for details). So the check for buttonType UIButtonTypeDetailDisclosure would fail (on iOS 7).
I have populated a couple of locations on MKMapView using MKPointAnnotation(s). On tapping annotations I am showing some options with UIActionSheet menu. The options have some delete functionality which would delete the selected annotation on map when user taps the delete option on UIActionSheet. The issue is that I am not able to determine which annotation point is clicked, I seem to have no reference to it.
The code that adds annotation point is:
while(looping array of locations)
{
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = {coord of my location}
annotationPoint.title = [anObject objectForKey:#"castTitle"];
annotationPoint.subtitle = [anObject objectForKey:#"storeName"];
[self.mainMapView addAnnotation:annotationPoint];
}
The code to show UIActionSheet on tapping annotation is:
-(MKAnnotationView*)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
pinView.pinColor = [self getAnnotationColor];
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[rightButton addTarget:self action:#selector(showOptions:) forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
-(IBAction)showOptions:(id)sender
{
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(#"", #"") delegate:self cancelButtonTitle:NSLocalizedString(#"Cancel", #"Cancel") destructiveButtonTitle:nil otherButtonTitles:NSLocalizedString(#"Delete", #"Delete"), nil];
[sheet showInView:[self.view window]];
}
You can also Create a class which inherits from MKPointAnnotation, and add an id property , and use your class :
#interface MyPointAnnotation : MKPointAnnotation
#property int pointId;
#end
and then use it in your view controller:
create annotation:
MyPointAnnotation *myAnnotation = [[MyPointAnnotation alloc]init];
myAnnotation.coordinate= someCoordinates;
[myAnnotation setTitle:#"i am annotation with id"];
myAnnotation.pointId = 1;
[self.mapView addAnnotation:myAnnotation];
if you have an array of coordinates you can loop and create annotations.
and you can even customize annotation view by its id:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *view=(MKPinAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:#"reuseme"];
if (!view) {
view=[[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"reuseme"];
}
if (((MyPointAnnotation *)annotation).pointId == 1)
{
//the annotation with id 1.
}
return view;
}
Seems like there are two approaches.
If only one annotation can be selected at a time, you could access the -selectedAnnotations property of the enclosing MKMapView.
Another approach is to inspect sender in showOptions:, which is a reference to the UIButton that triggered the action. Find out its enclosing MKAnnotationView, which will give you the associated -annotation. You could then either stash this as an ivar or (my preferred approach) use some runtime magic - in the form of objc_setAssociatedObject(), declared in <objc/runtime.h> - to attach a reference to the annotation to the action sheet, allowing easy retrieval in its delegate callback.
(You could actually do this back in the button creation phase if you wanted, and attach a reference to the annotation to the UIButton, which can be picked up directly in showOptions: and reattached to the action sheet.
But [MKMapView selectedAnnotations] I would think is the easier way to go if it suits your needs.
I have a map view with pins annotations into it. I need to pass to the detail view the coordinates of a pin when the user press the disclosure button of the pin information. How can I get the coordinates of the pin when I am into the showDetails method? I am using the next code.
- (void)showDetails:(id)sender {
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil ];
// HERE I NEED TO PASS THE COORDINATES OF THE PIN TO THE DETAILVIEWCONTROLLER.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
else { // Handles the other annotations.
// Try to dequeue an existing pin view first.
static NSString *AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if (!pinView) {
// If an existing pin view was not available, creates one.
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// Adds a detail disclosure button.
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
} else
pinView.annotation = annotation;
}
return nil;
}
Thanks for reading.
I cannot give you a working code solution right now since i don't have my mac here. But I can tell you about a possible approach.
Create a method in DetailViewController that populates its attributes, so you can call it from showDetails, before you push it. So the sequence of events would be like this.
Click on disclosure button -> showDetails called -> DetailViewController created and populated -> push DetailViewController
Hope this helped you
You can your UIButton button custom like this :
#interface CustomButton : UIButton {
CLLocationCoordinate2D pinCoordinate;
}
Now in your viewForAnnotation :
// Adds a detail disclosure button.
CustomButton *rightButton = [CustomButton buttonWithType:UIButtonTypeDetailDisclosure];
rightButton.pinCoordinate = annotation.coordinate
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = rightButton;
And finally to get the coordinate in the showDetails method :
CLLocationCoordinate2D currentCoord = ((CustomButton*)sender).pinCoordinate;