I am adding a button to annotation callout. On click event of button, I want to push another View Controller with details of annotation like title and subtitle. However, the problem is I am not able to get these details.
Here's what I did so far:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)userannotation
{
if ([annotation isMemberOfClass:[MKUserLocation class]])
{
return nil;
}
MKPinAnnotationView *v =[[MKPinAnnotationView alloc] initWithAnnotation:userannotation reuseIdentifier:#"ku"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
button.frame = CGRectMake(0, 0, 24, 24);
// [button addTarget:self action:#selector(callOutPressed:) forControlEvents:UIControlEventTouchUpInside];
v.rightCalloutAccessoryView = button;
v.canShowCallout = YES;
return v;
}
/*
- (void) callOutPressed:(id)sender
{
[ dict setValue:annotation.subtitle forKey:#"jid"] ;
[dict setValue:annotation.title forKey:#"name"];
NSLog(#"annotation values - %#",dict); // this shows null
*/
Okay. I tried using this:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"display name :-%#", view.annotation.title);// shows name
[dict setValue:view.annotation.title forKey:#"name"];//dict shows 0 key/value pairs and the app crashes.
NSLog(#"output :-%#", dict); //shows null
}
What is the correct way to do it?
Related
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKAnnotationView *view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:#"loc"];
return view;
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
for (MKAnnotationView *annotationView in views) {
annotationView.canShowCallout = NO;
}
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
MAKRCalloutView *calloutView = [[MAKRCalloutView alloc] initWithNibName:#"ViewM" bundle:nil];
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(10.0, 10.0, 50.0, 50.0)];
[btn setBackgroundColor:[UIColor redColor]];
[btn addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:calloutView.view];
[view addSubview:btn];
}
-(void) buttonClicked:(id)sender{
NSLog(#" %ld button click %# ", (long)[sender tag]);
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view {
for (UIView *subview in view.subviews) {
MAKRCalloutView *calloutView = [[MAKRCalloutView alloc] initWithNibName:#"ViewM" bundle:nil];
if (![subview isKindOfClass:[calloutView.view class]]) {
continue;
}
[subview removeFromSuperview];
}
}
I cannot call get button click event. Even I cannot click on button also. I want to go at other UIViewController by click on button.
I have also tried UITapGestureRecognizer. But its limited to Pin area only.
[button addTarget:self action:#selector(clickEvent) forControlEvents:UIControlEventTouchDown]
And then
- (CustomAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[CustomAnnotation class]])
{
CustomAnnotationView *newAnnotationView = nil;
// determine the type of annotation, and produce the correct type of annotation view for it.
newAnnotationView = [[[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil delegate: targetController] autorelease];
[newAnnotationView setEnabled:YES];
[newAnnotationView setCanShowCallout:NO];
return newAnnotationView;
}
}
#interface CustomAnnotationView : MKAnnotationView
{
}
#end
#implementation CustomAnnotationView
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier delegate:(id)targetController
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
/* Set initial width as you want. ex: 50X50*/
self.frame = CGRectMake(0, 0, Initial_width, Initial_height);
self.backgroundColor = [UIColor redColor];
return self;
}
/* When you tap on an annotation this method is fired*/
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if(selected){
/* When you select annotation, instead of showing default callout you can increase the width &height of annotation and add your custom view here*/
self.frame = CGRectMake(0, 0, max_width, max_height);
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(6, 0, max_width-10, max_height)];
view.backgroundColor = [UIColor blackColor];
UIButton *but = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *img = [UIImage imageNamed:#"image.png"];
but = CGRectMake(x , y, img.size.width + 20, img.size.height + 15);
[but setImage:img forState:UIControlStateNormal];
[but addTarget:targetController action:#selector(methodName) forControlEvents:UIControlEventTouchDown];
[view addSubview:but];
[self addSubview:view];
}
else {
/* When you tap outside of annotation, set the initial frame for the annotation. and remove the custom view from annotation*/
self.frame = CGRectMake(0, 0, Initial_width, Initial_height);
[view removeFromSuperview];
}
}
When I choose from a tableView, it goes back to my map and display the custom annotation callout over the pin selected. If I tap on another pin on the map, the custom callout view disappears from from the previous pin and is displayed over the current one selected.
If i try to tap inside the custom calloutView it also disappears. How can I avoid this, and why does this happen?
Here is the code:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if (![view.annotation isKindOfClass:[MKUserLocation class]]) {
self.calloutView = [[TNCalloutView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 100.0f)];
self.calloutView.alpha = 1.0f;
self.calloutView.backgroundColor = [UIColor whiteColor];
self.calloutView.layer.cornerRadius = 10.0f;
CGRect calloutViewFrame = self.calloutView.frame;
calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width/2 + 20 , -calloutViewFrame.size.height -10);
self.calloutView.frame = calloutViewFrame;
[view addSubview:self.calloutView];
}
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
if ([view.annotation isKindOfClass:[TNAnnotations class]]) {
for (UIView *subviews in view.subviews) {
[subviews removeFromSuperview];
}
}
}
If I remove the deselect method all together. Nothing ever goes away, and that's not what I want.
Also, I have checked out other posts about this very topic, but it didn't help me.
Cheers.
I am trying to make an application where various pins are placed on the map.
When I touch a pin, the standard bubble pops up with a disclosure button. When I click my disclosure button, I would like it to open up a new ViewController so I can display more information.
The problem is that every time I run the application, it crashes and it gives me an error in the output.
How could I fix this?
The error in the output is:
2013-06-29 16:51:33.575 ...[2723:13d03] -[FirstViewController
...Clicked:]: unrecognized selector sent to instance 0x867d0d0
2013-06-29 16:51:33.576 lam[2723:13d03] * Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason:
'-[FirstViewController ...Clicked:]: unrecognized selector sent
to instance 0x867d0d0'
* First throw call stack: (0x1d8d012 0x11cae7e 0x1e184bd 0x1d7cbbc 0x1d7c94e 0x11de705 0x182c0 0x18258 0xd9021 0xd957f 0xd86e8 0x47cef
0x47f02 0x25d4a 0x17698 0x1ce8df9 0x1ce8ad0 0x1d02bf5 0x1d02962
0x1d33bb6 0x1d32f44 0x1d32e1b 0x1ce77e3 0x1ce7668 0x14ffc 0x1e12
0x1d45) libc++abi.dylib: terminate called throwing an exception
The code in my FirstViewController.m is:
[...]
[...]
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKPinAnnotationView *MyPin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
MyPin.pinColor = MKPinAnnotationColorPurple;
UIButton *advertButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[annotation title] isEqualToString:#"..."]) {
[advertButton addTarget:self action:#selector(...Clicked:) forControlEvents:UIControlEventTouchUpInside];
}
if ([[annotation title] isEqualToString:#"..."]) {
[advertButton addTarget:self action:#selector(...Clicked:) forControlEvents:UIControlEventTouchUpInside];
}
MyPin.rightCalloutAccessoryView = advertButton;
MyPin.draggable = NO;
MyPin.highlighted = YES;
MyPin.animatesDrop=TRUE;
MyPin.canShowCallout = YES;
return MyPin;
}
- (void)mapView:(MKMapView *)mapView MyPin:(MKPinAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
// Go to edit view
UIViewController *detailViewController = [[UIViewController alloc] initWithNibName:#"...Controller" bundle:nil];
[self.navigationController pushViewController:detailViewController animated:YES];
}
//-(void) ...Clicked:(id)sender {
// NSLog(#"... Clicked");
//}
-(void) ...:(id)sender {
NSLog(#"...");
}
-(IBAction)findmylocation:(id)sender {
mapView.showsUserLocation = YES;
mapView.delegate = self;
[mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
}
What causes the error is:
- (void)mapView:(MKMapView *)mapView MyPin:(MKPinAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
// Go to edit view
UIViewController *detailViewController = [[UIViewController alloc] initWithNibName:#"..." bundle:nil];
[self.navigationController pushViewController:detailViewController animated:YES];
}
EDIT 2:
I have removed the advertButton addTarget lines, but now I don't have the disclosure button when I run the simulator... is what I removed correct?
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKPinAnnotationView *MyPin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
MyPin.pinColor = MKPinAnnotationColorPurple;
UIButton *advertButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[annotation title] isEqualToString:#"..."])
if ([[annotation title] isEqualToString:#"..."])
The error:
[FirstViewController ...Clicked:]: unrecognized selector sent to instance
means that it's trying to call the ...Clicked: method on FirstViewController (because you set the advertButton's target to that method) but there is no such method (you've commented it out).
You could just un-comment it.
But since you are using the calloutAccessoryControlTapped delegate method anyway to detect accessory taps (a better choice than a custom method), you should instead do the following:
Remove the advertButton addTarget lines from viewForAnnotation and just handle the taps in calloutAccessoryControlTapped.
Also remove the ...Clicked: method.
Another issue is the method calloutAccessoryControlTapped is incorrectly named as mapView:MyPin:calloutAccessoryControlTapped:.
The method must be named mapView:annotationView:calloutAccessoryControlTapped: like this:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// Go to edit view
UIViewController *detailViewController = [[UIViewController alloc] initWithNibName:#"...Controller" bundle:nil];
[self.navigationController pushViewController:detailViewController animated:YES];
}
By the way, in the calloutAccessoryControlTapped, you'll be able to access the annotation that was tapped through view.annotation.
The updated viewForAnnotation should be something like this:
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKPinAnnotationView *MyPin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
MyPin.pinColor = MKPinAnnotationColorPurple;
UIButton *advertButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
MyPin.rightCalloutAccessoryView = advertButton;
MyPin.draggable = NO;
MyPin.highlighted = YES;
MyPin.animatesDrop=TRUE;
MyPin.canShowCallout = YES;
return MyPin;
}
I try to call a method when the annotation view (and only the annotation view) is clicked.
- (void)mapView:(MKMapView *)m didSelectAnnotationView:(MKAnnotationView *)view {
[self openDetailView];
}
But what i noticed, is that this method (openDetailView) is called only when i select the PIN, means before the annotation view gets displayed. How can i fire that method on callout click? Thanx in advance.
By adding button with arrow instead of callout:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isMemberOfClass:[MKUserLocation class]]) {
return nil;
}
MKPinAnnotationView *v =[[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"ku"] autorelease];
UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
button.frame = CGRectMake(0, 0, 24, 24);
Shop *shop = [(ShopAnnotation *)annotation myShop];
button.tag = [shop.ID intValue];
[button addTarget:self action:#selector(callOutPressed:) forControlEvents:UIControlEventTouchUpInside];
[button setImage:[UIImage imageNamed:#"map_arr_right.png"] forState:UIControlStateNormal];
v.rightCalloutAccessoryView = button;
v.canShowCallout = YES;
return v;
}
- (void) callOutPressed:(id)sender {
NSLog(#"callout pressed");
}
Here's a sample custom callout that I'd want to get a similar style of. I was thinking of inheriting from MKAnnotation but I'm lost how to start it and I don't know if the callout's design could be overridden.
Any ideas how to implement this custom callout with ui controls inside?
EDIT: Here's my code from following the similar StackOverflow answer:
- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if(annotationView)
return annotationView;
else
{
UIImage *img = [UIImage imageNamed:#"default_thumb.png"];
MKAnnotationView *annotationView =
[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
annotationView.canShowCallout = YES;
annotationView.image = img;
annotationView.draggable = YES;
/*
// change left accessory view button with image
UIImageView *leftAccView = [[UIImageView alloc] initWithImage:img];
annotationView.leftCalloutAccessoryView = leftAccView;
//include a right button to show more info
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(calloutSubMenu:) forControlEvents:UIControlEventTouchUpInside];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
annotationView.rightCalloutAccessoryView = rightButton;
*/
return annotationView;
}
return nil;
}
// Customize accessory view
- (void)mapView:(MKMapView *)mapview annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[mapview deselectAnnotation:view.annotation animated:YES];
PopOverCallout *popOverCallout = [[PopOverCallout alloc]init];
UIPopoverController *popOver = [[UIPopoverController alloc] initWithContentViewController:popOverCallout];
self.annotationPopoverController = popOver;
popOver.popoverContentSize = CGSizeMake(500, 500);
[popOver presentPopoverFromRect:view.bounds inView:view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
The
(void)mapView:(MKMapView *)mapview annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
should be called whenever the pin is touched to show the callout right? but what happens is that a blank regular callout is shown. What might I be doing wrong?
BTW: the UIViewController subclass only has a label on it's XIB and is pretty much blank.
Found out the answer after a few mins of nap.
using this as the guide, I just needed to override
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
which shows the annotation callout. Thanks again for the answer, Ms. Anna.