Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Make tap gesture recognizer fail if it doesn’t do anything #7246

Merged
merged 7 commits into from
Dec 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Notification names and user info keys are now string enumeration values for ease of use in Swift. ([#6794](https://github.com/mapbox/mapbox-gl-native/pull/6794))
* MGLMapDebugOverdrawVisualizationMask no longer has any effect in Release builds of the SDK. This debug mask has been disabled for performance reasons. ([#5555](https://github.com/mapbox/mapbox-gl-native/pull/5555))
* Fixed a typo in the documentation for the MGLCompassDirectionFormatter class. ([#5879](https://github.com/mapbox/mapbox-gl-native/pull/5879))
* The UITapGestureRecognizer on MGLMapView that is used for selecting annotations now fails if a tap does not select an annotation. ([#7246](https://github.com/mapbox/mapbox-gl-native/pull/7246))

## 3.3.6 - November 9, 2016

Expand Down
64 changes: 47 additions & 17 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,8 @@ @interface MGLMapView () <UIGestureRecognizerDelegate,
@property (nonatomic, readwrite) UIButton *attributionButton;
@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints;
@property (nonatomic) UIActionSheet *attributionSheet;

@property (nonatomic, readwrite) MGLStyle *style;

@property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer;
@property (nonatomic) UIPanGestureRecognizer *pan;
@property (nonatomic) UIPinchGestureRecognizer *pinch;
@property (nonatomic) UIRotationGestureRecognizer *rotate;
Expand Down Expand Up @@ -502,9 +501,10 @@ - (void)commonInit
doubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:doubleTap];

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
[singleTap requireGestureRecognizerToFail:doubleTap];
[self addGestureRecognizer:singleTap];
_singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
[_singleTapGestureRecognizer requireGestureRecognizerToFail:doubleTap];
_singleTapGestureRecognizer.delegate = self;
[self addGestureRecognizer:_singleTapGestureRecognizer];

UITapGestureRecognizer *twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTapGesture:)];
twoFingerTap.numberOfTouchesRequired = 2;
Expand Down Expand Up @@ -1398,6 +1398,27 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
return;
}

id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:singleTap persistingResults:YES];
if(annotation)
{
[self selectAnnotation:annotation animated:YES];
}
else
{
[self deselectAnnotation:self.selectedAnnotation animated:YES];
}
}

/**
Returns the annotation that would be selected by a tap gesture recognizer.

This is used when a gesture is recognized, and to check if the gesture should be recognized.

@param singleTap An in progress tap gesture recognizer.
@param persist True to remember the cycleable set of annotations. @see annotationTagAtPoint:persistingResults
*/
- (nullable id <MGLAnnotation>)annotationForGestureRecognizer:(UITapGestureRecognizer*)singleTap persistingResults:(BOOL)persist
{
CGPoint tapPoint = [singleTap locationInView:self];

if (self.userLocationVisible)
Expand All @@ -1412,19 +1433,19 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
// Get the tap point within the custom hit test layer.
tapPointForUserLocation = [singleTap locationInView:self.userLocationAnnotationView];
}

CALayer *hitLayer = [self.userLocationAnnotationView.hitTestLayer hitTest:tapPointForUserLocation];

if (hitLayer)
{
if ( ! _userLocationAnnotationIsSelected)
{
[self selectAnnotation:self.userLocation animated:YES];
return self.userLocation;
}
return;
return nil;
}
}

// Handle the case of an offset annotation view by converting the tap point to be the geo location
// of the annotation itself that the view represents
for (MGLAnnotationView *view in self.annotationContainerView.annotationViews)
Expand All @@ -1436,21 +1457,19 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
}
}
}

MGLAnnotationTag hitAnnotationTag = [self annotationTagAtPoint:tapPoint persistingResults:YES];
MGLAnnotationTag hitAnnotationTag = [self annotationTagAtPoint:tapPoint persistingResults:persist];
if (hitAnnotationTag != MGLAnnotationTagNotFound)
{
if (hitAnnotationTag != _selectedAnnotationTag)
{
id <MGLAnnotation> annotation = [self annotationWithTag:hitAnnotationTag];
NSAssert(annotation, @"Cannot select nonexistent annotation with tag %u", hitAnnotationTag);
[self selectAnnotation:annotation animated:YES];
return annotation;
}
}
else
{
[self deselectAnnotation:self.selectedAnnotation animated:YES];
}

return nil;
}

- (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap
Expand Down Expand Up @@ -1608,6 +1627,17 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
}
}
}
else if (gestureRecognizer == _singleTapGestureRecognizer)
{
//Gesture will be recognized if it could deselect an annotation
if(!self.selectedAnnotation)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if an annotation is selected and tapping would ordinarily deselect it? Wouldn't this prevent the single tap gesture recognizer from firing to deselect the annotation?

Copy link
Contributor Author

@JesseCrocker JesseCrocker Dec 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is false, it returns YES from the last line of the method, so the recognizer runs

{
id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO];
if(!annotation) {
return NO;
}
}
}
return YES;
}

Expand Down
1 change: 1 addition & 0 deletions platform/macos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
* Fixed an issue where the map view’s center would always be calculated as if the view occupied the entire window. ([#6102](https://github.com/mapbox/mapbox-gl-native/pull/6102))
* Notification names and user info keys are now string enumeration values for ease of use in Swift. ([#6794](https://github.com/mapbox/mapbox-gl-native/pull/6794))
* Fixed a typo in the documentation for the MGLCompassDirectionFormatter class. ([#5879](https://github.com/mapbox/mapbox-gl-native/pull/5879))
* The NSClickGestureRecognizer on MGLMapView that is used for selecting annotations now fails if a click does not select an annotation. ([#7246](https://github.com/mapbox/mapbox-gl-native/pull/7246))

## 0.2.1 - July 19, 2016

Expand Down
24 changes: 20 additions & 4 deletions platform/macos/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ void MGLinitializeRunLoop() {
NSString *imageReuseIdentifier;
};

@interface MGLMapView () <NSPopoverDelegate, MGLMultiPointDelegate>
@interface MGLMapView () <NSPopoverDelegate, MGLMultiPointDelegate, NSGestureRecognizerDelegate>

@property (nonatomic, readwrite) NSSegmentedControl *zoomControls;
@property (nonatomic, readwrite) NSSlider *compass;
Expand All @@ -172,6 +172,7 @@ @implementation MGLMapView {
NSPanGestureRecognizer *_panGestureRecognizer;
NSMagnificationGestureRecognizer *_magnificationGestureRecognizer;
NSRotationGestureRecognizer *_rotationGestureRecognizer;
NSClickGestureRecognizer *_singleClickRecognizer;
double _scaleAtBeginningOfGesture;
CLLocationDirection _directionAtBeginningOfGesture;
CGFloat _pitchAtBeginningOfGesture;
Expand Down Expand Up @@ -408,9 +409,10 @@ - (void)installGestureRecognizers {
_panGestureRecognizer.delaysKeyEvents = YES;
[self addGestureRecognizer:_panGestureRecognizer];

NSClickGestureRecognizer *clickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleClickGesture:)];
clickGestureRecognizer.delaysPrimaryMouseButtonEvents = NO;
[self addGestureRecognizer:clickGestureRecognizer];
_singleClickRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleClickGesture:)];
_singleClickRecognizer.delaysPrimaryMouseButtonEvents = NO;
_singleClickRecognizer.delegate = self;
[self addGestureRecognizer:_singleClickRecognizer];

NSClickGestureRecognizer *rightClickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightClickGesture:)];
rightClickGestureRecognizer.buttonMask = 0x2;
Expand Down Expand Up @@ -1540,6 +1542,20 @@ - (NSView *)subviewContainingGesture:(NSGestureRecognizer *)gestureRecognizer {
return nil;
}

#pragma mark NSGestureRecognizerDelegate methods
- (BOOL)gestureRecognizer:(NSGestureRecognizer *)gestureRecognizer shouldAttemptToRecognizeWithEvent:(NSEvent *)event {
if (gestureRecognizer == _singleClickRecognizer) {
if (!self.selectedAnnotation) {
NSPoint gesturePoint = [self convertPoint:[event locationInWindow] fromView:nil];
MGLAnnotationTag hitAnnotationTag = [self annotationTagAtPoint:gesturePoint persistingResults:NO];
if (hitAnnotationTag == MGLAnnotationTagNotFound) {
return NO;
}
}
}
return YES;
}

#pragma mark Keyboard events

- (void)keyDown:(NSEvent *)event {
Expand Down