summaryrefslogtreecommitdiff
path: root/platform/ios/src/MGLMapView.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src/MGLMapView.mm')
-rw-r--r--platform/ios/src/MGLMapView.mm101
1 files changed, 74 insertions, 27 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index d188ffb91f..258f6eea87 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -152,9 +152,6 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1;
const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);
-/// Padding to edge of view that an offscreen annotation must have when being brought onscreen (by being selected)
-const UIEdgeInsets MGLMapViewOffscreenAnnotationPadding = UIEdgeInsetsMake(-20.0f, -20.0f, -20.0f, -20.0f);
-
/// An indication that the requested annotation was not found or is nonexistent.
enum { MGLAnnotationTagNotFound = UINT32_MAX };
@@ -229,6 +226,11 @@ public:
/// Currently shown popover representing the selected annotation.
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
+
+/// Anchor coordinate from which to present callout views (for example, for shapes this
+/// could be the touch point rather than its centroid)
+@property (nonatomic) CLLocationCoordinate2D anchorCoordinateForSelectedAnnotation;
+
@property (nonatomic) MGLUserLocationAnnotationView *userLocationAnnotationView;
/// Indicates how thoroughly the map view is tracking the user location.
@@ -4537,7 +4539,7 @@ public:
- (BOOL)isMovingAnnotationIntoViewSupportedForAnnotation:(id<MGLAnnotation>)annotation animated:(BOOL)animated {
// Consider delegating
- return animated && [annotation isKindOfClass:[MGLPointAnnotation class]];
+ return [annotation isKindOfClass:[MGLPointAnnotation class]];
}
- (id <MGLAnnotation>)selectedAnnotation
@@ -4644,12 +4646,15 @@ public:
CGPoint originPoint = [self convertCoordinate:origin toPointToView:self];
calloutPositioningRect = { .origin = originPoint, .size = CGSizeZero };
}
-
- CGRect expandedPositioningRect = UIEdgeInsetsInsetRect(calloutPositioningRect, MGLMapViewOffscreenAnnotationPadding);
+
+ CGRect expandedPositioningRect = calloutPositioningRect;
// Used for callout positioning, and moving offscreen annotations onscreen.
- CGRect constrainedRect = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
+ CGRect constrainedRect = self.contentFrame;
+ CGRect bounds = constrainedRect;
+ BOOL expandedPositioningRectToMoveCalloutIntoViewWithMargins = NO;
+
UIView <MGLCalloutView> *calloutView = nil;
if ([annotation respondsToSelector:@selector(title)] &&
@@ -4720,37 +4725,55 @@ public:
if (moveIntoView && [calloutView respondsToSelector:@selector(marginInsetsHintForPresentationFromRect:)]) {
UIEdgeInsets margins = [calloutView marginInsetsHintForPresentationFromRect:calloutPositioningRect];
expandedPositioningRect = UIEdgeInsetsInsetRect(expandedPositioningRect, margins);
+ expandedPositioningRectToMoveCalloutIntoViewWithMargins = YES;
}
}
+
+ if (!expandedPositioningRectToMoveCalloutIntoViewWithMargins)
+ {
+ // We don't have a callout (OR our callout didn't implement
+ // marginInsetsHintForPresentationFromRect: - in this case we need to
+ // ensure that partially off-screen annotations are NOT moved into view.
+ //
+ // We may want to create (and fallback to) an `MGLMapViewDelegate` version
+ // of the `-[MGLCalloutView marginInsetsHintForPresentationFromRect:]
+ // protocol method.
+ bounds = CGRectInset(bounds, -calloutPositioningRect.size.width, -calloutPositioningRect.size.height);
+ }
if (moveIntoView)
{
moveIntoView = NO;
- // Need to consider the content insets.
- CGRect bounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
-
// Any one of these cases should trigger a move onscreen
- if (CGRectGetMinX(calloutPositioningRect) < CGRectGetMinX(bounds))
- {
- constrainedRect.origin.x = expandedPositioningRect.origin.x;
+ CGFloat minX = CGRectGetMinX(expandedPositioningRect);
+
+ if (minX < CGRectGetMinX(bounds)) {
+ constrainedRect.origin.x = minX;
moveIntoView = YES;
}
- else if (CGRectGetMaxX(calloutPositioningRect) > CGRectGetMaxX(bounds))
- {
- constrainedRect.origin.x = CGRectGetMaxX(expandedPositioningRect) - constrainedRect.size.width;
- moveIntoView = YES;
+ else {
+ CGFloat maxX = CGRectGetMaxX(expandedPositioningRect);
+
+ if (maxX > CGRectGetMaxX(bounds)) {
+ constrainedRect.origin.x = maxX - CGRectGetWidth(constrainedRect);
+ moveIntoView = YES;
+ }
}
- if (CGRectGetMinY(calloutPositioningRect) < CGRectGetMinY(bounds))
- {
- constrainedRect.origin.y = expandedPositioningRect.origin.y;
+ CGFloat minY = CGRectGetMinY(expandedPositioningRect);
+
+ if (minY < CGRectGetMinY(bounds)) {
+ constrainedRect.origin.y = minY;
moveIntoView = YES;
}
- else if (CGRectGetMaxY(calloutPositioningRect) > CGRectGetMaxY(bounds))
- {
- constrainedRect.origin.y = CGRectGetMaxY(expandedPositioningRect) - constrainedRect.size.height;
- moveIntoView = YES;
+ else {
+ CGFloat maxY = CGRectGetMaxY(expandedPositioningRect);
+
+ if (maxY > CGRectGetMaxY(bounds)) {
+ constrainedRect.origin.y = maxY - CGRectGetHeight(constrainedRect);
+ moveIntoView = YES;
+ }
}
}
@@ -4760,6 +4783,17 @@ public:
constrainedToRect:constrainedRect
animated:animateSelection];
+ // Save the anchor coordinate
+ if ([annotation isKindOfClass:[MGLPointAnnotation class]]) {
+ self.anchorCoordinateForSelectedAnnotation = annotation.coordinate;
+ }
+ else {
+ // This is used for features like polygons, so that if the map is dragged
+ // the callout doesn't ping to its coordinate.
+ CGPoint anchorPoint = CGPointMake(CGRectGetMidX(calloutPositioningRect), CGRectGetMidY(calloutPositioningRect));
+ self.anchorCoordinateForSelectedAnnotation = [self convertPoint:anchorPoint toCoordinateFromView:self];
+ }
+
// notify delegate
if ([self.delegate respondsToSelector:@selector(mapView:didSelectAnnotation:)])
{
@@ -4824,8 +4858,18 @@ public:
return CGRectNull;
}
+ CLLocationCoordinate2D coordinate;
+
+ if ((annotation == self.selectedAnnotation) &&
+ CLLocationCoordinate2DIsValid(self.anchorCoordinateForSelectedAnnotation)) {
+ coordinate = self.anchorCoordinateForSelectedAnnotation;
+ }
+ else {
+ coordinate = annotation.coordinate;
+ }
+
if ([annotation isKindOfClass:[MGLMultiPoint class]]) {
- CLLocationCoordinate2D origin = annotation.coordinate;
+ CLLocationCoordinate2D origin = coordinate;
CGPoint originPoint = [self convertCoordinate:origin toPointToView:self];
return CGRectMake(originPoint.x, originPoint.y, MGLAnnotationImagePaddingForHitTest, MGLAnnotationImagePaddingForHitTest);
}
@@ -4840,7 +4884,7 @@ public:
return CGRectZero;
}
- CGRect positioningRect = [self frameOfImage:image centeredAtCoordinate:annotation.coordinate];
+ CGRect positioningRect = [self frameOfImage:image centeredAtCoordinate:coordinate];
positioningRect.origin.x -= 0.5;
return CGRectInset(positioningRect, -MGLAnnotationImagePaddingForCallout,
@@ -4895,6 +4939,7 @@ public:
// clean up
self.calloutViewForSelectedAnnotation = nil;
self.selectedAnnotation = nil;
+ self.anchorCoordinateForSelectedAnnotation = kCLLocationCoordinate2DInvalid;
// notify delegate
if ([self.delegate respondsToSelector:@selector(mapView:didDeselectAnnotation:)])
@@ -6158,7 +6203,9 @@ public:
annotationView = self.userLocationAnnotationView;
}
- CGRect positioningRect = annotationView ? annotationView.frame : [self positioningRectForCalloutForAnnotationWithTag:tag];
+ CGRect positioningRect = annotationView ?
+ annotationView.frame :
+ [self positioningRectForCalloutForAnnotationWithTag:tag];
MGLAssert( ! CGRectIsNull(positioningRect), @"Positioning rect should not be CGRectNull by this point");