diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-10-20 16:54:40 -0400 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-10-31 16:38:17 -0400 |
commit | 87c8150e6d64441bc3e4b72279933a9c9687d61c (patch) | |
tree | fd5dd91218a4ca9d91bbc566a6843f034be930d4 | |
parent | 372698aa23d2fb371df6790aa96d83bdf735edc9 (diff) | |
download | qtlocation-mapboxgl-87c8150e6d64441bc3e4b72279933a9c9687d61c.tar.gz |
[ios, macos] Center annotation's anchor to tap point when coordinate center is offscreen.
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 59 | ||||
-rw-r--r-- | platform/macos/src/MGLMapView.mm | 14 |
2 files changed, 45 insertions, 28 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index a7d2e17ce9..764b6a9fbf 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1615,7 +1615,9 @@ public: id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:singleTap persistingResults:YES]; if(annotation) { - [self selectAnnotation:annotation animated:YES]; + CGPoint calloutPoint = [singleTap locationInView:self]; + CGRect positionRect = [self positioningRectForAnnotation:annotation withDefaultCalloutPoint:calloutPoint]; + [self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect]; } else { @@ -3980,6 +3982,12 @@ public: - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated { + CGRect positioningRect = [self positioningRectForAnnotation:annotation withDefaultCalloutPoint:CGPointZero]; + [self selectAnnotation:annotation animated:animated calloutPositioningRect:positioningRect]; +} + +- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated calloutPositioningRect:(CGRect)calloutPositioningRect +{ if ( ! annotation) return; if (annotation == self.selectedAnnotation) return; @@ -3995,9 +4003,6 @@ public: if (annotationTag == MGLAnnotationTagNotFound) return; } - // By default attempt to use the GL annotation image frame as the positioning rect. - CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag]; - MGLAnnotationView *annotationView = nil; if (annotation != self.userLocation) @@ -4005,21 +4010,12 @@ public: MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); annotationView = annotationContext.annotationView; if (annotationView && annotationView.enabled) { - { - // Annotations represented by views use the view frame as the positioning rect. - positioningRect = annotationView.frame; - [annotationView.superview bringSubviewToFront:annotationView]; - [annotationView setSelected:YES animated:animated]; + // Annotations represented by views use the view frame as the positioning rect. + calloutPositioningRect = annotationView.frame; + [annotationView.superview bringSubviewToFront:annotationView]; + [annotationView setSelected:YES animated:animated]; } } - } - - // The client can request that any annotation be selected (even ones that are offscreen). - // The annotation can’t be selected if no part of it is hittable. - if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation) - { - return; - } self.selectedAnnotation = annotation; @@ -4049,7 +4045,7 @@ public: if (_userLocationAnnotationIsSelected) { - positioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame]; + calloutPositioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame]; CGRect implicitAnnotationFrame = [self.userLocationAnnotationView.layer.presentationLayer frame]; CGRect explicitAnnotationFrame = self.userLocationAnnotationView.frame; @@ -4065,7 +4061,7 @@ public: if ([calloutView.leftAccessoryView isKindOfClass:[UIControl class]]) { UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(handleCalloutAccessoryTapGesture:)]; + action:@selector(handleCalloutAccessoryTapGesture:)]; [calloutView.leftAccessoryView addGestureRecognizer:calloutAccessoryTap]; } @@ -4078,7 +4074,7 @@ public: if ([calloutView.rightAccessoryView isKindOfClass:[UIControl class]]) { UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(handleCalloutAccessoryTapGesture:)]; + action:@selector(handleCalloutAccessoryTapGesture:)]; [calloutView.rightAccessoryView addGestureRecognizer:calloutAccessoryTap]; } @@ -4088,7 +4084,7 @@ public: calloutView.delegate = self; // present popup - [calloutView presentCalloutFromRect:positioningRect + [calloutView presentCalloutFromRect:calloutPositioningRect inView:self.glView constrainedToView:self.glView animated:animated]; @@ -4118,6 +4114,27 @@ public: /// Returns the rectangle that represents the annotation image of the annotation /// with the given tag. This rectangle is fitted to the image’s alignment rect /// and is appropriate for positioning a popover. +/// If a shape annotation is visible but its centroid is not, and a default point is specified, +/// the callout view is anchored to the default callout point. +- (CGRect)positioningRectForAnnotation:(id <MGLAnnotation>)annotation withDefaultCalloutPoint:(CGPoint)calloutPoint +{ + MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation]; + CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag]; + + // For annotations which `coordinate` falls offscreen it will use the current tap point as anchor instead. + if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation) + { + if (!CGPointEqualToPoint(calloutPoint, CGPointZero)) { + positioningRect = CGRectMake(calloutPoint.x, calloutPoint.y, positioningRect.size.width, positioningRect.size.height); + } + } + + return positioningRect; +} + +/// Returns the rectangle that represents the annotation image of the annotation +/// with the given tag. This rectangle is fitted to the image’s alignment rect +/// and is appropriate for positioning a popover. - (CGRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annotationTag { id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag]; diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 1bb30cc464..8df6f4545d 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -1488,7 +1488,7 @@ public: if (hitAnnotationTag != _selectedAnnotationTag) { id <MGLAnnotation> annotation = [self annotationWithTag:hitAnnotationTag]; NSAssert(annotation, @"Cannot select nonexistent annotation with tag %u", hitAnnotationTag); - [self selectAnnotation:annotation]; + [self selectAnnotation:annotation atPoint:gesturePoint]; } } else { [self deselectAnnotation:self.selectedAnnotation]; @@ -2216,11 +2216,11 @@ public: - (void)selectAnnotation:(id <MGLAnnotation>)annotation { - // Only point annotations can be selected. - if (!annotation || [annotation isKindOfClass:[MGLMultiPoint class]]) { - return; - } + [self selectAnnotation:annotation atPoint:NSZeroPoint]; +} +- (void)selectAnnotation:(id <MGLAnnotation>)annotation atPoint:(NSPoint)gesturePoint +{ id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation; if (annotation == selectedAnnotation) { return; @@ -2235,10 +2235,10 @@ public: [self addAnnotation:annotation]; } - // The annotation can’t be selected if no part of it is hittable. + // The annotation's anchor will bounce to the current click. NSRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag]; if (NSIsEmptyRect(NSIntersectionRect(positioningRect, self.bounds))) { - return; + positioningRect = CGRectMake(gesturePoint.x, gesturePoint.y, positioningRect.size.width, positioningRect.size.height); } self.selectedAnnotation = annotation; |