Authored by Thomas Rasch

o Unified gesture recognizing in the map view (closes Alpstein/route-me#56)

... ... @@ -8,28 +8,9 @@
#import <UIKit/UIKit.h>
@class RMMapOverlayView, RMAnnotation;
@protocol RMMapOverlayViewDelegate <NSObject>
@optional
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView tapOnAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint;
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView doubleTapOnAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint;
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView tapOnLabelForAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint;
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView doubleTapOnLabelForAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint;
- (BOOL)mapOverlayView:(RMMapOverlayView *)aMapOverlayView shouldDragAnnotation:(RMAnnotation *)anAnnotation;
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView didDragAnnotation:(RMAnnotation *)anAnnotation withDelta:(CGPoint)delta;
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView didEndDragAnnotation:(RMAnnotation *)anAnnotation;
@end
@class RMAnnotation;
@interface RMMapOverlayView : UIView
{
id <RMMapOverlayViewDelegate> delegate;
}
@property (nonatomic, assign) id <RMMapOverlayViewDelegate> delegate;
- (unsigned)sublayersCount;
... ...
... ... @@ -11,21 +11,7 @@
#import "RMAnnotation.h"
#import "RMPixel.h"
@interface RMMapOverlayView ()
- (void)handleSingleTap:(UIGestureRecognizer *)recognizer;
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer;
@end
@implementation RMMapOverlayView
{
BOOL _trackPanGesture;
CGPoint _lastTranslation;
RMAnnotation *_draggedAnnotation;
}
@synthesize delegate;
+ (Class)layerClass
{
... ... @@ -39,33 +25,9 @@
self.layer.masksToBounds = YES;
_trackPanGesture = NO;
_lastTranslation = CGPointZero;
_draggedAnnotation = nil;
UITapGestureRecognizer *doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)] autorelease];
doubleTapRecognizer.numberOfTapsRequired = 2;
UITapGestureRecognizer *singleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)] autorelease];
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
UIPanGestureRecognizer *panGestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)] autorelease];
panGestureRecognizer.minimumNumberOfTouches = 1;
panGestureRecognizer.maximumNumberOfTouches = 1;
[self addGestureRecognizer:singleTapRecognizer];
[self addGestureRecognizer:doubleTapRecognizer];
[self addGestureRecognizer:panGestureRecognizer];
return self;
}
- (void)dealloc
{
[_draggedAnnotation release]; _draggedAnnotation = nil;
[super dealloc];
}
- (unsigned)sublayersCount
{
return [self.layer.sublayers count];
... ... @@ -96,135 +58,4 @@
[self.layer scrollPoint:CGPointMake(-delta.x, -delta.y)];
}
#pragma mark -
#pragma mark Event handling
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if ([[event touchesForView:self] count] > 1)
return NO;
CALayer *hit = [self.layer hitTest:point];
RMAnnotation *hitAnnotation = [self findAnnotationInLayer:hit];
if (!hit || !hitAnnotation || ![hitAnnotation.layer isKindOfClass:[RMMarker class]])
return NO;
return hitAnnotation.enabled;
}
- (RMAnnotation *)findAnnotationInLayer:(CALayer *)layer
{
if ([layer respondsToSelector:@selector(annotation)])
return [((RMMarker *)layer) annotation];
CALayer *superlayer = [layer superlayer];
if (superlayer != nil && [superlayer respondsToSelector:@selector(annotation)])
return [((RMMarker *)superlayer) annotation];
else if ([superlayer superlayer] != nil && [[superlayer superlayer] respondsToSelector:@selector(annotation)])
return [((RMMarker *)[superlayer superlayer]) annotation];
return nil;
}
- (void)handleSingleTap:(UIGestureRecognizer *)recognizer
{
CALayer *hit = [self.layer hitTest:[recognizer locationInView:self]];
if (!hit)
return;
CALayer *superlayer = [hit superlayer];
// See if tap was on a marker or marker label and send delegate protocol method
if ([hit isKindOfClass:[RMMarker class]])
{
if ([delegate respondsToSelector:@selector(mapOverlayView:tapOnAnnotation:atPoint:)])
[delegate mapOverlayView:self tapOnAnnotation:[((RMMarker *)hit) annotation] atPoint:[recognizer locationInView:self]];
}
else if (superlayer != nil && [superlayer isKindOfClass:[RMMarker class]])
{
if ([delegate respondsToSelector:@selector(mapOverlayView:tapOnLabelForAnnotation:atPoint:)])
[delegate mapOverlayView:self tapOnLabelForAnnotation:[((RMMarker *)superlayer) annotation] atPoint:[recognizer locationInView:self]];
}
else if ([superlayer superlayer] != nil && [[superlayer superlayer] isKindOfClass:[RMMarker class]])
{
if ([delegate respondsToSelector:@selector(mapOverlayView:tapOnLabelForAnnotation:atPoint:)])
[delegate mapOverlayView:self tapOnLabelForAnnotation:[((RMMarker *)[superlayer superlayer]) annotation] atPoint:[recognizer locationInView:self]];
}
}
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
CALayer *hit = [self.layer hitTest:[recognizer locationInView:self]];
if (!hit)
return;
CALayer *superlayer = [hit superlayer];
// See if tap was on a marker or marker label and send delegate protocol method
if ([hit isKindOfClass:[RMMarker class]])
{
if ([delegate respondsToSelector:@selector(mapOverlayView:doubleTapOnAnnotation:atPoint:)])
[delegate mapOverlayView:self doubleTapOnAnnotation:[((RMMarker *)hit) annotation] atPoint:[recognizer locationInView:self]];
}
else if (superlayer != nil && [superlayer isKindOfClass:[RMMarker class]])
{
if ([delegate respondsToSelector:@selector(mapOverlayView:doubleTapOnLabelForAnnotation:atPoint:)])
[delegate mapOverlayView:self doubleTapOnLabelForAnnotation:[((RMMarker *)superlayer) annotation] atPoint:[recognizer locationInView:self]];
}
else if ([superlayer superlayer] != nil && [[superlayer superlayer] isKindOfClass:[RMMarker class]])
{
if ([delegate respondsToSelector:@selector(mapOverlayView:doubleTapOnLabelForAnnotation:atPoint:)])
[delegate mapOverlayView:self doubleTapOnLabelForAnnotation:[((RMMarker *)[superlayer superlayer]) annotation] atPoint:[recognizer locationInView:self]];
}
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
CALayer *hit = [self.layer hitTest:[recognizer locationInView:self]];
if (!hit)
return;
if ([hit respondsToSelector:@selector(enableDragging)] && ![(RMMarker *)hit enableDragging])
return;
_lastTranslation = CGPointZero;
[_draggedAnnotation release];
_draggedAnnotation = [[self findAnnotationInLayer:hit] retain];
if ([delegate respondsToSelector:@selector(mapOverlayView:shouldDragAnnotation:)])
_trackPanGesture = [delegate mapOverlayView:self shouldDragAnnotation:_draggedAnnotation];
else
_trackPanGesture = NO;
}
if (!_trackPanGesture)
return;
if (recognizer.state == UIGestureRecognizerStateChanged && [delegate respondsToSelector:@selector(mapOverlayView:didDragAnnotation:withDelta:)])
{
CGPoint translation = [recognizer translationInView:self];
CGPoint delta = CGPointMake(_lastTranslation.x - translation.x, _lastTranslation.y - translation.y);
_lastTranslation = translation;
[CATransaction begin];
[CATransaction setAnimationDuration:0];
[delegate mapOverlayView:self didDragAnnotation:_draggedAnnotation withDelta:delta];
[CATransaction commit];
}
else if (recognizer.state == UIGestureRecognizerStateEnded && [delegate respondsToSelector:@selector(mapOverlayView:didEndDragAnnotation:)])
{
[delegate mapOverlayView:self didEndDragAnnotation:_draggedAnnotation];
_trackPanGesture = NO;
[_draggedAnnotation release]; _draggedAnnotation = nil;
}
}
@end
... ...
... ... @@ -8,24 +8,10 @@
#import "RMTileSource.h"
@class RMMapView, RMMapTiledLayerView;
@protocol RMMapTiledLayerViewDelegate <NSObject>
@optional
// points are in the mapview coordinate space
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView singleTapAtPoint:(CGPoint)aPoint;
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView doubleTapAtPoint:(CGPoint)aPoint;
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView twoFingerSingleTapAtPoint:(CGPoint)aPoint;
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView twoFingerDoubleTapAtPoint:(CGPoint)aPoint;
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView longPressAtPoint:(CGPoint)aPoint;
@end
@class RMMapView;
@interface RMMapTiledLayerView : UIView
@property (nonatomic, assign) id <RMMapTiledLayerViewDelegate> delegate;
@property (nonatomic, assign) BOOL useSnapshotRenderer;
@property (nonatomic, readonly) id <RMTileSource> tileSource;
... ...
... ... @@ -12,20 +12,12 @@
#import "RMTileSource.h"
#import "RMTileImage.h"
@interface RMMapOverlayView ()
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer;
- (void)handleTwoFingerDoubleTap:(UIGestureRecognizer *)recognizer;
@end
@implementation RMMapTiledLayerView
{
RMMapView *_mapView;
id <RMTileSource> _tileSource;
}
@synthesize delegate = _delegate;
@synthesize useSnapshotRenderer = _useSnapshotRenderer;
@synthesize tileSource = _tileSource;
... ... @@ -44,13 +36,11 @@
if (!(self = [super initWithFrame:frame]))
return nil;
self.opaque = NO;
_mapView = [aMapView retain];
_tileSource = [aTileSource retain];
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = YES;
self.opaque = NO;
self.useSnapshotRenderer = NO;
CATiledLayer *tiledLayer = [self tiledLayer];
... ... @@ -59,28 +49,6 @@
tiledLayer.levelsOfDetail = levelsOf2xMagnification;
tiledLayer.levelsOfDetailBias = levelsOf2xMagnification;
UITapGestureRecognizer *doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)] autorelease];
doubleTapRecognizer.numberOfTapsRequired = 2;
UITapGestureRecognizer *singleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)] autorelease];
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerDoubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDoubleTap:)] autorelease];
twoFingerDoubleTapRecognizer.numberOfTapsRequired = 2;
twoFingerDoubleTapRecognizer.numberOfTouchesRequired = 2;
UITapGestureRecognizer *twoFingerSingleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerSingleTap:)] autorelease];
twoFingerSingleTapRecognizer.numberOfTouchesRequired = 2;
[twoFingerSingleTapRecognizer requireGestureRecognizerToFail:twoFingerDoubleTapRecognizer];
UILongPressGestureRecognizer *longPressRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)] autorelease];
[self addGestureRecognizer:singleTapRecognizer];
[self addGestureRecognizer:doubleTapRecognizer];
[self addGestureRecognizer:twoFingerDoubleTapRecognizer];
[self addGestureRecognizer:twoFingerSingleTapRecognizer];
[self addGestureRecognizer:longPressRecognizer];
return self;
}
... ... @@ -234,39 +202,4 @@
[pool release]; pool = nil;
}
#pragma mark -
#pragma mark Event handling
- (void)handleSingleTap:(UIGestureRecognizer *)recognizer
{
if ([_delegate respondsToSelector:@selector(mapTiledLayerView:singleTapAtPoint:)])
[_delegate mapTiledLayerView:self singleTapAtPoint:[recognizer locationInView:_mapView]];
}
- (void)handleTwoFingerSingleTap:(UIGestureRecognizer *)recognizer
{
if ([_delegate respondsToSelector:@selector(mapTiledLayerView:twoFingerSingleTapAtPoint:)])
[_delegate mapTiledLayerView:self twoFingerSingleTapAtPoint:[recognizer locationInView:_mapView]];
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer
{
if (recognizer.state != UIGestureRecognizerStateBegan) return;
if ([_delegate respondsToSelector:@selector(mapTiledLayerView:longPressAtPoint:)])
[_delegate mapTiledLayerView:self longPressAtPoint:[recognizer locationInView:_mapView]];
}
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
if ([_delegate respondsToSelector:@selector(mapTiledLayerView:doubleTapAtPoint:)])
[_delegate mapTiledLayerView:self doubleTapAtPoint:[recognizer locationInView:_mapView]];
}
- (void)handleTwoFingerDoubleTap:(UIGestureRecognizer *)recognizer
{
if ([_delegate respondsToSelector:@selector(mapTiledLayerView:twoFingerDoubleTapAtPoint:)])
[_delegate mapTiledLayerView:self twoFingerDoubleTapAtPoint:[recognizer locationInView:_mapView]];
}
@end
... ...
... ... @@ -64,8 +64,7 @@ typedef enum : NSUInteger {
} RMMapDecelerationMode;
@interface RMMapView : UIView <UIScrollViewDelegate, RMMapOverlayViewDelegate,
RMMapTiledLayerViewDelegate, RMMapScrollViewDelegate>
@interface RMMapView : UIView <UIScrollViewDelegate, UIGestureRecognizerDelegate, RMMapScrollViewDelegate>
@property (nonatomic, assign) id <RMMapViewDelegate> delegate;
... ...
... ... @@ -119,6 +119,9 @@
BOOL _mapScrollViewIsZooming;
BOOL _enableDragging, _enableBouncing;
CGPoint _lastDraggingTranslation;
RMAnnotation *_draggedAnnotation;
}
@synthesize decelerationMode = _decelerationMode;
... ... @@ -149,6 +152,9 @@
_constrainMovement = _enableBouncing = _zoomingInPivotsAroundCenter = NO;
_enableDragging = YES;
_lastDraggingTranslation = CGPointZero;
_draggedAnnotation = nil;
self.backgroundColor = [UIColor grayColor];
_tileSourcesContainer = [RMTileSourcesContainer new];
... ... @@ -293,6 +299,7 @@
[self setDelegate:nil];
[self setBackgroundView:nil];
[self setQuadTree:nil];
[_draggedAnnotation release]; _draggedAnnotation = nil;
[_annotations release]; _annotations = nil;
[_visibleAnnotations release]; _visibleAnnotations = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
... ... @@ -925,11 +932,11 @@
_mapScrollView.contentOffset = CGPointMake(0.0, 0.0);
_tiledLayersSuperview = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height)];
_tiledLayersSuperview.userInteractionEnabled = NO;
for (id <RMTileSource> tileSource in _tileSourcesContainer.tileSources)
{
RMMapTiledLayerView *tiledLayerView = [[RMMapTiledLayerView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) mapView:self forTileSource:tileSource];
tiledLayerView.delegate = self;
if (self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength * 2.0, tileSideLength * 2.0);
... ... @@ -950,7 +957,6 @@
_mapScrollView.mapScrollViewDelegate = self;
_mapScrollView.zoomScale = exp2f([self zoom]);
[self setDecelerationMode:_decelerationMode];
if (_backgroundView)
... ... @@ -959,10 +965,52 @@
[self insertSubview:_mapScrollView atIndex:0];
_overlayView = [[RMMapOverlayView alloc] initWithFrame:[self bounds]];
_overlayView.delegate = self;
_overlayView.userInteractionEnabled = NO;
[self insertSubview:_overlayView aboveSubview:_mapScrollView];
// add gesture recognizers
// one finger taps
UITapGestureRecognizer *doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)] autorelease];
doubleTapRecognizer.numberOfTouchesRequired = 1;
doubleTapRecognizer.numberOfTapsRequired = 2;
UITapGestureRecognizer *singleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)] autorelease];
singleTapRecognizer.numberOfTouchesRequired = 1;
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
UILongPressGestureRecognizer *longPressRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)] autorelease];
[self addGestureRecognizer:singleTapRecognizer];
[self addGestureRecognizer:doubleTapRecognizer];
[self addGestureRecognizer:longPressRecognizer];
// two finger taps
UITapGestureRecognizer *twoFingerDoubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDoubleTap:)] autorelease];
twoFingerDoubleTapRecognizer.numberOfTapsRequired = 2;
twoFingerDoubleTapRecognizer.numberOfTouchesRequired = 2;
UITapGestureRecognizer *twoFingerSingleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerSingleTap:)] autorelease];
twoFingerSingleTapRecognizer.numberOfTouchesRequired = 2;
[twoFingerSingleTapRecognizer requireGestureRecognizerToFail:twoFingerDoubleTapRecognizer];
[self addGestureRecognizer:twoFingerSingleTapRecognizer];
[self addGestureRecognizer:twoFingerDoubleTapRecognizer];
// pan
UIPanGestureRecognizer *panGestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)] autorelease];
panGestureRecognizer.minimumNumberOfTouches = 1;
panGestureRecognizer.maximumNumberOfTouches = 1;
// the delegate is used to decide whether a pan should be handled by this
// recognizer or by the pan gesture recognizer of the scrollview
panGestureRecognizer.delegate = self;
// the pan recognizer is added to the scrollview as it competes with the
// pan recognizer of the scrollview
[_mapScrollView addGestureRecognizer:panGestureRecognizer];
[_visibleAnnotations removeAllObjects];
[self correctPositionOfAllAnnotations];
}
... ... @@ -1025,127 +1073,6 @@
[_delegate afterMapZoom:self];
}
// Overlay
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView tapOnAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasTapOnAnnotation && anAnnotation)
{
[_delegate tapOnAnnotation:anAnnotation onMap:self];
}
else
{
if (_delegateHasSingleTapOnMap)
[_delegate singleTapOnMap:self at:aPoint];
}
}
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView doubleTapOnAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasDoubleTapOnAnnotation && anAnnotation)
{
[_delegate doubleTapOnAnnotation:anAnnotation onMap:self];
}
else
{
[self mapTiledLayerView:[_tiledLayersSuperview.subviews lastObject] doubleTapAtPoint:aPoint];
}
}
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView tapOnLabelForAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasTapOnLabelForAnnotation && anAnnotation)
{
[_delegate tapOnLabelForAnnotation:anAnnotation onMap:self];
}
else if (_delegateHasTapOnAnnotation && anAnnotation)
{
[_delegate tapOnAnnotation:anAnnotation onMap:self];
}
else
{
if (_delegateHasSingleTapOnMap)
[_delegate singleTapOnMap:self at:aPoint];
}
}
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView doubleTapOnLabelForAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasDoubleTapOnLabelForAnnotation && anAnnotation)
{
[_delegate doubleTapOnLabelForAnnotation:anAnnotation onMap:self];
}
else if (_delegateHasDoubleTapOnAnnotation && anAnnotation)
{
[_delegate doubleTapOnAnnotation:anAnnotation onMap:self];
}
else
{
[self mapTiledLayerView:[_tiledLayersSuperview.subviews lastObject] doubleTapAtPoint:aPoint];
}
}
- (BOOL)mapOverlayView:(RMMapOverlayView *)aMapOverlayView shouldDragAnnotation:(RMAnnotation *)anAnnotation
{
if (_delegateHasShouldDragMarker)
return [_delegate mapView:self shouldDragAnnotation:anAnnotation];
else
return NO;
}
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView didDragAnnotation:(RMAnnotation *)anAnnotation withDelta:(CGPoint)delta
{
if (_delegateHasDidDragMarker)
[_delegate mapView:self didDragAnnotation:anAnnotation withDelta:delta];
}
- (void)mapOverlayView:(RMMapOverlayView *)aMapOverlayView didEndDragAnnotation:(RMAnnotation *)anAnnotation
{
if (_delegateHasDidEndDragMarker)
[_delegate mapView:self didEndDragAnnotation:anAnnotation];
}
// Tiled layer
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView singleTapAtPoint:(CGPoint)aPoint
{
if (_delegateHasSingleTapOnMap)
[_delegate singleTapOnMap:self at:aPoint];
}
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView doubleTapAtPoint:(CGPoint)aPoint
{
if (self.zoomingInPivotsAroundCenter)
[self zoomInToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
else
[self zoomInToNextNativeZoomAt:aPoint animated:YES];
if (_delegateHasDoubleTapOnMap)
[_delegate doubleTapOnMap:self at:aPoint];
}
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView twoFingerDoubleTapAtPoint:(CGPoint)aPoint
{
[self zoomOutToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
if (_delegateHasDoubleTapTwoFingersOnMap)
[_delegate doubleTapTwoFingersOnMap:self at:aPoint];
}
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView twoFingerSingleTapAtPoint:(CGPoint)aPoint
{
[self zoomOutToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
if (_delegateHasSingleTapTwoFingersOnMap)
[_delegate singleTapTwoFingersOnMap:self at:aPoint];
}
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView longPressAtPoint:(CGPoint)aPoint
{
if (_delegateHasLongSingleTapOnMap)
[_delegate longSingleTapOnMap:self at:aPoint];
}
// Detect dragging/zooming
- (void)scrollView:(RMMapScrollView *)aScrollView correctedContentOffset:(inout CGPoint *)aContentOffset
... ... @@ -1285,6 +1212,260 @@
[_delegate mapViewRegionDidChange:self];
}
#pragma mark - Gesture Recognizers and event handling
- (RMAnnotation *)findAnnotationInLayer:(CALayer *)layer
{
if ([layer respondsToSelector:@selector(annotation)])
return [((RMMarker *)layer) annotation];
CALayer *superlayer = [layer superlayer];
if (superlayer != nil && [superlayer respondsToSelector:@selector(annotation)])
return [((RMMarker *)superlayer) annotation];
else if ([superlayer superlayer] != nil && [[superlayer superlayer] respondsToSelector:@selector(annotation)])
return [((RMMarker *)[superlayer superlayer]) annotation];
return nil;
}
- (void)singleTapAtPoint:(CGPoint)aPoint
{
if (_delegateHasSingleTapOnMap)
[_delegate singleTapOnMap:self at:aPoint];
}
- (void)handleSingleTap:(UIGestureRecognizer *)recognizer
{
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:_overlayView]];
if ( ! hit)
{
[self singleTapAtPoint:[recognizer locationInView:self]];
return;
}
CALayer *superlayer = [hit superlayer];
// See if tap was on a marker or marker label and send delegate protocol method
if ([hit isKindOfClass:[RMMarker class]])
{
[self tapOnAnnotation:[((RMMarker *)hit) annotation] atPoint:[recognizer locationInView:self]];
}
else if (superlayer != nil && [superlayer isKindOfClass:[RMMarker class]])
{
[self tapOnLabelForAnnotation:[((RMMarker *)superlayer) annotation] atPoint:[recognizer locationInView:self]];
}
else if ([superlayer superlayer] != nil && [[superlayer superlayer] isKindOfClass:[RMMarker class]])
{
[self tapOnLabelForAnnotation:[((RMMarker *)[superlayer superlayer]) annotation] atPoint:[recognizer locationInView:self]];
}
else
{
[self singleTapAtPoint:[recognizer locationInView:self]];
}
}
- (void)doubleTapAtPoint:(CGPoint)aPoint
{
if (self.zoomingInPivotsAroundCenter)
[self zoomInToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
else
[self zoomInToNextNativeZoomAt:aPoint animated:YES];
if (_delegateHasDoubleTapOnMap)
[_delegate doubleTapOnMap:self at:aPoint];
}
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:_overlayView]];
if ( ! hit)
{
[self doubleTapAtPoint:[recognizer locationInView:self]];
return;
}
CALayer *superlayer = [hit superlayer];
// See if tap was on a marker or marker label and send delegate protocol method
if ([hit isKindOfClass:[RMMarker class]])
{
[self doubleTapOnAnnotation:[((RMMarker *)hit) annotation] atPoint:[recognizer locationInView:self]];
}
else if (superlayer != nil && [superlayer isKindOfClass:[RMMarker class]])
{
[self doubleTapOnLabelForAnnotation:[((RMMarker *)superlayer) annotation] atPoint:[recognizer locationInView:self]];
}
else if ([superlayer superlayer] != nil && [[superlayer superlayer] isKindOfClass:[RMMarker class]])
{
[self doubleTapOnLabelForAnnotation:[((RMMarker *)[superlayer superlayer]) annotation] atPoint:[recognizer locationInView:self]];
}
else
{
[self doubleTapAtPoint:[recognizer locationInView:self]];
}
}
- (void)handleTwoFingerDoubleTap:(UIGestureRecognizer *)recognizer
{
[self zoomOutToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
if (_delegateHasDoubleTapTwoFingersOnMap)
[_delegate doubleTapTwoFingersOnMap:self at:[recognizer locationInView:self]];
}
- (void)handleTwoFingerSingleTap:(UIGestureRecognizer *)recognizer
{
[self zoomOutToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
if (_delegateHasSingleTapTwoFingersOnMap)
[_delegate singleTapTwoFingersOnMap:self at:[recognizer locationInView:self]];
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer
{
if (recognizer.state != UIGestureRecognizerStateBegan)
return;
if (_delegateHasLongSingleTapOnMap)
[_delegate longSingleTapOnMap:self at:[recognizer locationInView:self]];
}
// defines when the additional pan gesture recognizer on the scroll should handle the gesture
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recognizer
{
if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
// check whether our custom pan gesture recognizer should start recognizing the gesture
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:_overlayView]];
if (!hit || ([hit respondsToSelector:@selector(enableDragging)] && ![(RMMarker *)hit enableDragging]))
return NO;
if ( ! [self shouldDragAnnotation:[self findAnnotationInLayer:hit]])
return NO;
}
return YES;
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:self]];
if ( ! hit)
return;
if ([hit respondsToSelector:@selector(enableDragging)] && ![(RMMarker *)hit enableDragging])
return;
_lastDraggingTranslation = CGPointZero;
[_draggedAnnotation release];
_draggedAnnotation = [[self findAnnotationInLayer:hit] retain];
}
if (recognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint translation = [recognizer translationInView:_overlayView];
CGPoint delta = CGPointMake(_lastDraggingTranslation.x - translation.x, _lastDraggingTranslation.y - translation.y);
_lastDraggingTranslation = translation;
[CATransaction begin];
[CATransaction setAnimationDuration:0];
[self didDragAnnotation:_draggedAnnotation withDelta:delta];
[CATransaction commit];
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
[self didEndDragAnnotation:_draggedAnnotation];
[_draggedAnnotation release]; _draggedAnnotation = nil;
}
}
// Overlay
- (void)tapOnAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasTapOnAnnotation && anAnnotation)
{
[_delegate tapOnAnnotation:anAnnotation onMap:self];
}
else
{
if (_delegateHasSingleTapOnMap)
[_delegate singleTapOnMap:self at:aPoint];
}
}
- (void)doubleTapOnAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasDoubleTapOnAnnotation && anAnnotation)
{
[_delegate doubleTapOnAnnotation:anAnnotation onMap:self];
}
else
{
[self doubleTapAtPoint:aPoint];
}
}
- (void)tapOnLabelForAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasTapOnLabelForAnnotation && anAnnotation)
{
[_delegate tapOnLabelForAnnotation:anAnnotation onMap:self];
}
else if (_delegateHasTapOnAnnotation && anAnnotation)
{
[_delegate tapOnAnnotation:anAnnotation onMap:self];
}
else
{
if (_delegateHasSingleTapOnMap)
[_delegate singleTapOnMap:self at:aPoint];
}
}
- (void)doubleTapOnLabelForAnnotation:(RMAnnotation *)anAnnotation atPoint:(CGPoint)aPoint
{
if (_delegateHasDoubleTapOnLabelForAnnotation && anAnnotation)
{
[_delegate doubleTapOnLabelForAnnotation:anAnnotation onMap:self];
}
else if (_delegateHasDoubleTapOnAnnotation && anAnnotation)
{
[_delegate doubleTapOnAnnotation:anAnnotation onMap:self];
}
else
{
[self doubleTapAtPoint:aPoint];
}
}
- (BOOL)shouldDragAnnotation:(RMAnnotation *)anAnnotation
{
if (_delegateHasShouldDragMarker)
return [_delegate mapView:self shouldDragAnnotation:anAnnotation];
else
return NO;
}
- (void)didDragAnnotation:(RMAnnotation *)anAnnotation withDelta:(CGPoint)delta
{
if (_delegateHasDidDragMarker)
[_delegate mapView:self didDragAnnotation:anAnnotation withDelta:delta];
}
- (void)didEndDragAnnotation:(RMAnnotation *)anAnnotation
{
if (_delegateHasDidEndDragMarker)
[_delegate mapView:self didEndDragAnnotation:anAnnotation];
}
#pragma mark -
#pragma mark Snapshots
... ... @@ -1423,7 +1604,6 @@
CGSize contentSize = CGSizeMake(tileSideLength, tileSideLength); // zoom level 1
RMMapTiledLayerView *tiledLayerView = [[RMMapTiledLayerView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) mapView:self forTileSource:newTileSource];
tiledLayerView.delegate = self;
if (self.adjustTilesForRetinaDisplay && _screenScale > 1.0)
((CATiledLayer *)tiledLayerView.layer).tileSize = CGSizeMake(tileSideLength * 2.0, tileSideLength * 2.0);
... ...