In my app I have a MapView with a few MKGeodesicPolylines. I want to be able to recognize touch gestures on these lines. The overlay are added using:
[_mapView addOverlay:polyline];
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay
{
if ([overlay class] == [MKGeodesicPolyline class])
{
MKPolylineRenderer *renderer = [[[MKPolylineRenderer alloc] initWithPolyline:overlay] autorelease];
renderer.lineWidth = 4.0;
renderer.strokeColor = [UIColor blackColor];
return renderer;
}
return nil;
}
I have tried WildcardGestureRecognizer which should fit my purpose. Here is the code I am using:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
.... MapView init ....
WildcardGestureRecognizer * tapInterceptor = [[WildcardGestureRecognizer alloc] init];
tapInterceptor.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:_mapView];
CLLocationCoordinate2D coord = [_mapView convertPoint:point toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(coord);
for (id overlay in _mapView.overlays)
{
if ([overlay isKindOfClass:[MKGeodesicPolyline class]])
{
MKGeodesicPolyline *poly = (MKGeodesicPolyline*) overlay;
id view = [_mapView viewForOverlay:poly];
NSLog(@"view class: %@",[view class]);
if ([view isKindOfClass:[MKPolylineRenderer class]])
{
MKPolylineRenderer *polyView = (MKPolylineRenderer*) view;
CGPoint polygonViewPoint = [polyView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
if (mapCoordinateIsInPolygon) {
NSLog(@"hit!");
} else {
NSLog(@"miss!");
}
}
}
}
};
[_mapView addGestureRecognizer:tapInterceptor];
}
return self;
}
The problem is the point of the code above where the first Log gets called. The class always seems to return null
. Log output:
2014-01-06 13:50:41.106 App[11826:60b] view class: (null)
I hope somebody could point me into the right direction. Thanks a lot in advance!
WORKING CODE:
I got it working for me. Hope it helps somebody else:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
.... MapView init ....
WildcardGestureRecognizer * tapInterceptor = [[WildcardGestureRecognizer alloc] init];
tapInterceptor.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
CGPoint point = [tapInterceptor locationInView:_mapView];
CLLocationCoordinate2D coord = [_mapView convertPoint:point toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(coord);
for (id overlay in _mapView.overlays)
{
if ([overlay isKindOfClass:[MKGeodesicPolyline class]])
{
MKGeodesicPolyline *poly = (MKGeodesicPolyline*) overlay;
id view = [_mapView viewForOverlay:poly];
NSLog(@"view class: %@",[view class]);
if ([view isKindOfClass:[MKPolylineRenderer class]])
{
MKPolylineRenderer *polyView = (MKPolylineRenderer*) view;
[polyView invalidatePath];
CGPoint polygonViewPoint = [polyView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
if (mapCoordinateIsInPolygon)
{
NSLog(@"hit!");
} else {
NSLog(@"miss!");
}
}
}
}
};
[_mapView addGestureRecognizer:tapInterceptor];
}
return self;
}
ALTERNATIVE:
Add UITapGestureRecognizer:
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[_mapView addGestureRecognizer:recognizer];
[recognizer release];
Handle gesture:
- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded)
{
for (int i=0; i<recognizer.numberOfTouches; i++)
{
//CGPoint point = [recognizer locationInView:_mapView];
CGPoint point = [recognizer locationOfTouch:i inView:_mapView];
CLLocationCoordinate2D coord = [_mapView convertPoint:point toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(coord);
for (id overlay in _mapView.overlays)
{
if ([overlay isKindOfClass:[MKGeodesicPolyline class]])
{
MKGeodesicPolyline *poly = (MKGeodesicPolyline*) overlay;
id view = [_mapView rendererForOverlay:poly];
if ([view isKindOfClass:[MKPolylineRenderer class]] && [[poly title] isEqualToString:@"fullRouteAbove"])
{
MKPolylineRenderer *polyView = (MKPolylineRenderer*) view;
[polyView invalidatePath];
CGPoint polygonViewPoint = [polyView pointForMapPoint:mapPoint];
NSLog(@"polyView: %@",polyView);
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
if (mapCoordinateIsInPolygon)
{
NSLog(@"hit!");
}else{
NSLog(@"miss!");
)
}
}
}
}
}
}
SWITCHING BETWEEN TOUCHABLE AREAS:
Replace
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
with
BOOL mapCoordinateIsInPolygon = CGRectContainsPoint(CGPathGetBoundingBox(polyView.path), polygonViewPoint);
or
BOOL mapCoordinateIsInPolygon = CGRectContainsPoint(CGPathGetPathBoundingBox(polyView.path), polygonViewPoint);
I think you need this:
Add an NSMutableArray called arrPolylineViews to your class.
Then add a tapGestureRecognizer to your mapView:
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] init];
gr.numberOfTapsRequired = 1;
gr.numberOfTouchesRequired = 1;
gr.delegate = self;
[gr addTarget:self action:@selector(didTap:)];
[myMapView addGestureRecognizer:gr];
In didTap:
- (void)didTap:(UITapGestureRecognizer *)gr
{
if (gr.state == UIGestureRecognizerStateEnded)
{
// convert the touch point to a CLLocationCoordinate & geocode
CGPoint touchPoint = [gr locationInView:myMapView];
MKPolylineView *touchedPolyLineView = [self polylineTapped:touchPoint];
if (touchedPolyLineView)
{
//touched a polyLineView
}
}
}
Then:
- (MKOverlayView*)mapView:(MKMapView*)theMapView viewForOverlay:(id <MKOverlay>)overlay
{
if([overlay isKindOfClass:[MKPolyline class]]){
//create your polyLineView
[arrPolylineViews addObject:polyLineView];
}
}
Then add this method:
- (MKPolylineView *)polylineTapped:(CGPoint)point
{
// Check if the overlay got tapped
for (MKPolylineView *polyView in arrPolylineViews)
{
// Get view frame rect in the mapView's coordinate system
CGRect viewFrameInMapView = [polyView.superview convertRect:polyView.frame toView:myMapView];
// Check if the touch is within the view bounds
if (CGRectContainsPoint(viewFrameInMapView, point))
{
return polyView;
}
}
return nil;
}
Don't forget to -
[arrPolylineViews removeAllObjects];
before adding the new list of points on map.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With