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.mm178
1 files changed, 134 insertions, 44 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 0849cbd12f..f2141c3840 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -209,6 +209,8 @@ public:
@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
@property (nonatomic) UIPanGestureRecognizer *twoFingerDrag;
+@property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask;
+
/// Mapping from reusable identifiers to annotation images.
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
@@ -502,10 +504,6 @@ public:
_doubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:_doubleTap];
- _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
- [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTap];
- _singleTapGestureRecognizer.delegate = self;
- [self addGestureRecognizer:_singleTapGestureRecognizer];
_twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)];
_twoFingerDrag.minimumNumberOfTouches = 2;
@@ -530,16 +528,25 @@ public:
[_quickZoom requireGestureRecognizerToFail:_doubleTap];
[self addGestureRecognizer:_quickZoom];
+ _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
+ [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTap];
+ _singleTapGestureRecognizer.delegate = self;
+ [_singleTapGestureRecognizer requireGestureRecognizerToFail:_quickZoom];
+ [self addGestureRecognizer:_singleTapGestureRecognizer];
+
// observe app activity
//
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationWillEnterForegroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationDidBecomeActiveNotification object:nil];
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+
// set initial position
//
mbgl::CameraOptions options;
@@ -547,6 +554,9 @@ public:
mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
options.padding = padding;
options.zoom = 0;
+
+ _cameraChangeReasonBitmask = MGLCameraChangeReasonNone;
+
_mbglMap->jumpTo(options);
_pendingLatitude = NAN;
_pendingLongitude = NAN;
@@ -1233,6 +1243,8 @@ public:
- (void)handleCompassTapGesture:(__unused id)sender
{
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonResetNorth;
+
[self resetNorthAnimated:YES];
if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading ||
@@ -1255,6 +1267,7 @@ public:
- (void)notifyGestureDidBegin {
BOOL animated = NO;
+
[self cameraWillChangeAnimated:animated];
_mbglMap->setGestureInProgress(true);
_changeDelimiterSuppressionDepth++;
@@ -1278,6 +1291,23 @@ public:
return _changeDelimiterSuppressionDepth > 0;
}
+- (BOOL)_shouldChangeFromCamera:(nonnull MGLMapCamera *)oldCamera toCamera:(nonnull MGLMapCamera *)newCamera
+{
+ // Check delegates first
+ if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:reason:)])
+ {
+ return [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:newCamera reason:self.cameraChangeReasonBitmask];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)])
+ {
+ return [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:newCamera];
+ }
+ else
+ {
+ return YES;
+ }
+}
+
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
if ( ! self.isScrollEnabled) return;
@@ -1285,7 +1315,9 @@ public:
_mbglMap->cancelTransitions();
MGLMapCamera *oldCamera = self.camera;
-
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGesturePan;
+
if (pan.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
@@ -1299,9 +1331,8 @@ public:
CGPoint delta = [pan translationInView:pan.view];
MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:delta panGesture:pan];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->moveBy({ delta.x, delta.y });
[pan setTranslation:CGPointZero inView:pan.view];
@@ -1323,9 +1354,8 @@ public:
{
CGPoint offset = CGPointMake(velocity.x * self.decelerationRate / 4, velocity.y * self.decelerationRate / 4);
MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:offset panGesture:pan];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->moveBy({ offset.x, offset.y }, MGLDurationFromTimeInterval(self.decelerationRate));
}
@@ -1356,6 +1386,8 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:pinch];
MGLMapCamera *oldCamera = self.camera;
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGesturePinch;
+
if (pinch.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
@@ -1372,9 +1404,8 @@ public:
// Calculates the final camera zoom, has no effect within current map camera.
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
// The gesture recognizer only reports the gesture’s current center
@@ -1426,9 +1457,8 @@ public:
// Calculates the final camera zoom, this has no effect within current map camera.
double zoom = log2(newScale);
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint];
-
- if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)]
- && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
drift = NO;
} else {
@@ -1454,7 +1484,9 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:rotate];
MGLMapCamera *oldCamera = self.camera;
-
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureRotate;
+
if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
@@ -1481,9 +1513,8 @@ public:
}
MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
@@ -1501,9 +1532,8 @@ public:
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationFromTimeInterval(decelerationRate));
@@ -1548,6 +1578,7 @@ public:
}
[self deselectAnnotation:self.selectedAnnotation animated:YES];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement);
+
return;
}
@@ -1558,7 +1589,7 @@ public:
CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint];
[self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect];
}
- else
+ else if (self.selectedAnnotation)
{
[self deselectAnnotation:self.selectedAnnotation animated:YES];
}
@@ -1640,6 +1671,8 @@ public:
if (doubleTap.state == UIGestureRecognizerStateEnded)
{
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomIn;
+
MGLMapCamera *oldCamera = self.camera;
double newZoom = round(self.zoomLevel) + 1.0;
@@ -1647,9 +1680,8 @@ public:
CGPoint gesturePoint = [self anchorPointForGesture:doubleTap];
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:gesturePoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
[self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
@@ -1676,9 +1708,13 @@ public:
_mbglMap->cancelTransitions();
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomOut;
+
if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
+
+ [self notifyGestureDidBegin];
}
else if (twoFingerTap.state == UIGestureRecognizerStateEnded)
{
@@ -1689,9 +1725,8 @@ public:
CGPoint gesturePoint = [self anchorPointForGesture:twoFingerTap];
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:gesturePoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
_mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration));
@@ -1711,7 +1746,9 @@ public:
if ( ! self.isZoomEnabled) return;
_mbglMap->cancelTransitions();
-
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureOneFingerZoom;
+
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
@@ -1734,9 +1771,8 @@ public:
MGLMapCamera *oldCamera = self.camera;
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
@@ -1756,6 +1792,8 @@ public:
_mbglMap->cancelTransitions();
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureTilt;
+
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
@@ -1775,8 +1813,7 @@ public:
MGLMapCamera *oldCamera = self.camera;
MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew];
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
@@ -2852,6 +2889,8 @@ public:
{
self.userTrackingMode = MGLUserTrackingModeNone;
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
[self _setCenterCoordinate:centerCoordinate edgePadding:self.contentInset zoomLevel:zoomLevel direction:direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil completionHandler:completion];
}
@@ -2901,6 +2940,9 @@ public:
}
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
_mbglMap->easeTo(cameraOptions, animationOptions);
}
@@ -2924,6 +2966,8 @@ public:
if (zoomLevel == self.zoomLevel) return;
_mbglMap->cancelTransitions();
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
CGFloat duration = animated ? MGLAnimationDuration : 0;
_mbglMap->setZoom(zoomLevel,
@@ -3012,6 +3056,9 @@ public:
- (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction duration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion
{
self.userTrackingMode = MGLUserTrackingModeNone;
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
[self _setVisibleCoordinates:coordinates count:count edgePadding:insets direction:direction duration:duration animationTimingFunction:function completionHandler:completion];
}
@@ -3061,6 +3108,9 @@ public:
[self willChangeValueForKey:@"visibleCoordinateBounds"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"visibleCoordinateBounds"];
}
@@ -3094,6 +3144,8 @@ public:
CGFloat duration = animated ? MGLAnimationDuration : 0;
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
if (self.userTrackingMode == MGLUserTrackingModeNone)
{
_mbglMap->setBearing(direction,
@@ -3178,6 +3230,9 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
@@ -3234,6 +3289,9 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets];
_mbglMap->flyTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
@@ -3394,6 +3452,13 @@ public:
return [self metersPerPointAtLatitude:latitude];
}
+#pragma mark - Camera Change Reason -
+
+- (void)resetCameraChangeReason
+{
+ self.cameraChangeReasonBitmask = MGLCameraChangeReasonNone;
+}
+
#pragma mark - Styling -
- (NS_ARRAY_OF(NSURL *) *)bundledStyleURLs
@@ -5340,9 +5405,16 @@ public:
}
}
- if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)])
+ if ( ! [self isSuppressingChangeDelimiters] )
{
- [self.delegate mapView:self regionWillChangeAnimated:animated];
+ if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeWithReason:animated:)])
+ {
+ [self.delegate mapView:self regionWillChangeWithReason:self.cameraChangeReasonBitmask animated:animated];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)])
+ {
+ [self.delegate mapView:self regionWillChangeAnimated:animated];
+ }
}
}
@@ -5356,8 +5428,12 @@ public:
if (!self.scaleBar.hidden) {
[(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]];
}
-
- if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)])
+
+ if ([self.delegate respondsToSelector:@selector(mapView:regionIsChangingWithReason:)])
+ {
+ [self.delegate mapView:self regionIsChangingWithReason:self.cameraChangeReasonBitmask];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)])
{
[self.delegate mapViewRegionIsChanging:self];
}
@@ -5370,9 +5446,13 @@ public:
[self updateCompass];
- if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)])
+ if ( ! [self isSuppressingChangeDelimiters])
{
- if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
+ BOOL respondsToSelector = [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)];
+ BOOL respondsToSelectorWithReason = [self.delegate respondsToSelector:@selector(mapView:regionDidChangeWithReason:animated:)];
+
+ if ((respondsToSelector || respondsToSelectorWithReason) &&
+ ([UIApplication sharedApplication].applicationState == UIApplicationStateActive))
{
_featureAccessibilityElements = nil;
_visiblePlaceFeatures = nil;
@@ -5384,7 +5464,17 @@ public:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
}
- [self.delegate mapView:self regionDidChangeAnimated:animated];
+
+ if (respondsToSelectorWithReason)
+ {
+ [self.delegate mapView:self regionDidChangeWithReason:self.cameraChangeReasonBitmask animated:animated];
+ }
+ else if (respondsToSelector)
+ {
+ [self.delegate mapView:self regionDidChangeAnimated:animated];
+ }
+
+ [self resetCameraChangeReason];
}
}