how do I detect a tap on the title of a callout of a annotation? I already have a right callout accessory and a left one, but I want to detect if a user taps on the title (which is in the center on the callout).
If this is not possible, how do I disable hiding the callout if I tap on the title?
it's little late to the answer your question but I'm dealing with same kind of problem recently and make my solution by my self with trial and error.
maybe I can help someone who dealing with same problem.
you can also use some custom annotation and callout view classes, there are many example out there.
but, there is a another simple way to solve this problem without use the complex foreign classes.
problem definition:
I want to be able to trigger some method just by touching in callout view.
I don't want to use right or left accesory buttons.
but when I touched callout view, view will disapear immediately.
short explanation of solution:
simply we use custom "MKPinAnnotationView" with "hittest" to detect touches in callout view.
you can also find github project link at the end of the post.
touchableCallOutsViewController.h
//
// touchableCallOutsViewController.h
// touchableCallOuts
//
// Created by yasin turkoglu on 20.11.2012.
// Copyright (c) 2012 yasin turkoglu. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@class myCustomPinAnnotationClass;
@interface touchableCallOutsViewController : UIViewController <MKMapViewDelegate> {
MKMapView *myMapView;
int pinCounter;
myCustomPinAnnotationClass *myAnnotation;
CLLocationCoordinate2D selectedPinCoordinate;
int selectedPinNumber;
}
@property (strong, nonatomic) MKMapView *myMapView;
@property (strong, nonatomic) myCustomPinAnnotationClass *myAnnotation;
@end
touchableCallOutsViewController.m
//
// touchableCallOutsViewController.m
// touchableCallOuts
//
// Created by yasin turkoglu on 20.11.2012.
// Copyright (c) 2012 yasin turkoglu. All rights reserved.
//
#import "touchableCallOutsViewController.h"
#import "myCustomPinAnnotationClass.h"
@interface touchableCallOutsViewController ()
@end
@implementation touchableCallOutsViewController
@synthesize myMapView;
@synthesize myAnnotation;
- (void)viewDidLoad
{
//first we create mapview and add pin annotation
myMapView = [[MKMapView alloc]initWithFrame:self.view.frame];
myMapView.delegate = self;
MKCoordinateRegion region = {{0,0},{1.0,1.0}};
region.center.latitude = 41.036651; //user defined
region.center.longitude = 28.983870;//user defined
[myMapView setRegion:region animated:YES];
[self.view addSubview:myMapView];
//we define long press recognizer for 2 seconds and add our map view to add new pin when you press 2 seconds on map view
UILongPressGestureRecognizer *lngPrs = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleMyLongPress:)];
lngPrs.minimumPressDuration = 2.0;
[myMapView addGestureRecognizer:lngPrs];
pinCounter = 1; //this variable hold pin numbers and increment 1 when new pin added.
myAnnotation = [[myCustomPinAnnotationClass alloc]initWithName:[NSString stringWithFormat:@"Pin %i",pinCounter] description:@"touch here to see pin coordinates" pinNum:pinCounter coordinate:region.center];
[myMapView addAnnotation:myAnnotation];
}
- (void)handleMyLongPress:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan) {
}else{
//when we press more than 2 seconds on map view UILongPressGestureRecognizer triggered this method and call our custom MKPinAnnotationView class and add annotation.
CGPoint touchPoint = [gestureRecognizer locationInView:myMapView];
CLLocationCoordinate2D touchMapCoordinate = [myMapView convertPoint:touchPoint toCoordinateFromView:myMapView];
pinCounter++;
myAnnotation = [[myCustomPinAnnotationClass alloc]initWithName:[NSString stringWithFormat:@"Pin %i",pinCounter] description:@"touch here to see pin coordinates" pinNum:pinCounter coordinate:touchMapCoordinate];
[myMapView addAnnotation:myAnnotation];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//when new annotation added by us, new annotation property will set here
static NSString *identifier = @"myPins";
if ([annotation isKindOfClass:[myCustomPinAnnotationClass class]]) {
myCustomPinAnnotationClass *annotationView = (myCustomPinAnnotationClass *) [self.myMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[myCustomPinAnnotationClass alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
myAnnotation = (myCustomPinAnnotationClass *)annotation;
annotationView.pinColor = MKPinAnnotationColorGreen;
annotationView.enabled = YES;
annotationView.draggable = YES;
annotationView.canShowCallout = YES;
[annotationView setSelected:YES animated:YES];
return annotationView;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
//if you select any annotation to show their callout sellected annotation pin number and coordinate setted and call findcallot method
myAnnotation = (myCustomPinAnnotationClass *)view.annotation;
selectedPinCoordinate = myAnnotation.coordinate;
selectedPinNumber = myAnnotation.pinNum;
[self findCallOut:myMapView];
}
- (void)findCallOut:(UIView *)node
{
//this method dig our mapview with for loop until find callout view class named "UICalloutView"
if([node isKindOfClass:[NSClassFromString(@"UICalloutView") class]]){
//this method trigered together with callout open animation.
//in UICalloutView we have callout bubble image
//we dig it with for loop to find this image height
float buubleHeight = 0.0;
for(UIImageView *bubbleComponents in node.subviews){
//when we find callout bubble image take height to set our touchable area height
buubleHeight = bubbleComponents.frame.size.height;
break;
}
//this method triger with callout open animation as I said before
//ant this bouncing open animation distort actual size of callout view
//so when this happens we also get transform value of callout view and make proportion to find exact sizes.
CGFloat nodeTransformRatio = node.transform.a;
CGFloat calculatedWidth = roundf(node.frame.size.width / nodeTransformRatio);
CGFloat calculatedHeight = roundf(buubleHeight / nodeTransformRatio);
//now create a new UIView and sized according to callout view.
UIView *touchableView = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, calculatedWidth, calculatedHeight)];
touchableView.userInteractionEnabled = YES;
//we add new single tap gesture recognizer to triger desired method when users touching to the callout
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doSomething)];
[touchableView addGestureRecognizer:singleTap];
//and finaly we add this newly created view in callout view to receive touches.
[node addSubview:touchableView];
}else{
//loop self until find a "UICalloutView"
for(UIView *child in node.subviews){
[self findCallOut:child];
}
}
}
- (void)doSomething
{
//this method call allert view our preseted variables when users touch callout view.
UIAlertView *alertHolder = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"You're touched the Pin %i CallOut",selectedPinNumber] message:[NSString stringWithFormat:@"lat : %f\nlong : %f",selectedPinCoordinate.latitude,selectedPinCoordinate.longitude] delegate:self cancelButtonTitle:nil otherButtonTitles:@"ok",nil];
[alertHolder show];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
myCustomPinAnnotationClass.h
//
// myCustomPinAnnotationClass.h
// touchableCallOuts
//
// Created by yasin turkoglu on 20.11.2012.
// Copyright (c) 2012 yasin turkoglu. All rights reserved.
//
#import <MapKit/MapKit.h>
@interface myCustomPinAnnotationClass : MKPinAnnotationView <MKAnnotation> {
NSString *_name;
NSString *_description;
int _pinNum;
CLLocationCoordinate2D _coordinate;
}
@property (copy) NSString *name;
@property (copy) NSString *description;
@property (nonatomic, readonly) int pinNum;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithName:(NSString*)header description:(NSString*)description pinNum:(int)pinNum coordinate:(CLLocationCoordinate2D)coordinate;
@end
myCustomPinAnnotationClass.m
//
// myCustomPinAnnotationClass.m
// touchableCallOuts
//
// Created by yasin turkoglu on 20.11.2012.
// Copyright (c) 2012 yasin turkoglu. All rights reserved.
//
#import "myCustomPinAnnotationClass.h"
@implementation myCustomPinAnnotationClass
@synthesize name = _name;
@synthesize description = _description;
@synthesize pinNum = _pinNum;
@synthesize coordinate = _coordinate;
- (id)initWithName:(NSString*)header description:(NSString*)description pinNum:(int)pinNum coordinate:(CLLocationCoordinate2D)coordinate
{
self = [super init];
if (self) {
_name = [header copy];
_description = [description copy];
_pinNum = pinNum;
_coordinate = coordinate;
}
return self;
}
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate
{
_coordinate = newCoordinate;
}
- (NSString *)title {
return _name;
}
- (NSString *)subtitle {
return _description;
}
- (int)tag {
return _pinNum;
}
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
//test touched point in map view
//when hit test return nil callout close immediately by default
UIView* hitView = [super hitTest:point withEvent:event];
// if hittest return nil test touch point
if (hitView == nil){
//dig view to find custom touchable view lately added by us
for(UIView *firstView in self.subviews){
if([firstView isKindOfClass:[NSClassFromString(@"UICalloutView") class]]){
for(UIView *touchableView in firstView.subviews){
if([touchableView isKindOfClass:[UIView class]]){ //this is our touchable view class
//define touchable area
CGRect touchableArea = CGRectMake(firstView.frame.origin.x, firstView.frame.origin.y, touchableView.frame.size.width, touchableView.frame.size.height);
//test touch point if in touchable area
if (CGRectContainsPoint(touchableArea, point)){
//if touch point is in touchable area return touchable view as a touched view
hitView = touchableView;
}
}
}
}
}
}
return hitView;
}
@end
Here you can download complete project listing with source code https://github.com/ytur/touchableCallOutsForIOSMaps