summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Rex <julian.rex@mapbox.com>2018-02-08 00:35:21 -0500
committerJulian Rex <julian.rex@mapbox.com>2018-02-09 16:33:29 -0500
commit5b6252ff06a3f2bdfd2c87789bceb79882cb1cef (patch)
treec4407ec1196f9e38fcb7536a28455dbd200d901c
parent5ad584af1b041591e2d5f2bb12364b0a890107c5 (diff)
downloadqtlocation-mapboxgl-5b6252ff06a3f2bdfd2c87789bceb79882cb1cef.tar.gz
[ios] Added:
- MGLCameraChangeReason constants - New camera change delegate methods with reason parameters. - mapView:didSingleTapAtCoordinate: delegate method
-rw-r--r--platform/ios/app/MBXViewController.m24
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj24
-rw-r--r--platform/ios/src/MGLCameraChange.h14
-rw-r--r--platform/ios/src/MGLCameraChange.m16
-rw-r--r--platform/ios/src/MGLMapView.mm196
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h97
-rw-r--r--platform/ios/test/MGLMapViewDelegateIntegrationTests.swift6
-rw-r--r--platform/ios/uitest/MapViewTests.m7
8 files changed, 329 insertions, 55 deletions
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 2c3d26b489..d452d152cc 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -1279,6 +1279,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)styleDynamicPointCollection
{
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(36.9979, -109.0441) zoomLevel:14 animated:NO];
+
CLLocationCoordinate2D coordinates[] = {
{37.00145594210082, -109.04960632324219},
{37.00173012609867, -109.0404224395752},
@@ -1879,15 +1880,34 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
_usingLocaleBasedCountryLabels = [[self bestLanguageForUser] isEqualToString:@"en"];
}
-- (void)mapViewRegionIsChanging:(MGLMapView *)mapView
+- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera reason:(MGLCameraChangeReason)reason
+{
+ return YES;
+}
+
+- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated reason:(MGLCameraChangeReason)reason
+{
+}
+
+- (void)mapViewRegionIsChanging:(MGLMapView *)mapView reason:(MGLCameraChangeReason)reason
{
[self updateHUD];
}
-- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated reason:(MGLCameraChangeReason)reason
+{
[self updateHUD];
}
+- (void)mapView:(MGLMapView *)mapView didSingleTapAtCoordinate:(CLLocationCoordinate2D)coordinate
+{
+#if 0
+ MGLPointAnnotation *annot = [[MGLPointAnnotation alloc] init];
+ annot.coordinate = coordinate;
+ [self.mapView addAnnotation:annot];
+#endif
+}
+
- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(MGLUserLocation *)userLocation {
[self updateHUD];
}
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 2325f3d3ce..1f193f0623 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -251,6 +251,10 @@
AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; };
AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; };
+ CA55CD3F202C16AA00CE7095 /* MGLCameraChange.m in Sources */ = {isa = PBXBuildFile; fileRef = CA55CD3D202C16A900CE7095 /* MGLCameraChange.m */; };
+ CA55CD40202C16AA00CE7095 /* MGLCameraChange.m in Sources */ = {isa = PBXBuildFile; fileRef = CA55CD3D202C16A900CE7095 /* MGLCameraChange.m */; };
+ CA55CD41202C16AA00CE7095 /* MGLCameraChange.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChange.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ CA55CD42202C16AA00CE7095 /* MGLCameraChange.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChange.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */; };
@@ -756,6 +760,8 @@
96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = "<group>"; };
AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = "<group>"; };
AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = "<group>"; };
+ CA55CD3D202C16A900CE7095 /* MGLCameraChange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCameraChange.m; sourceTree = "<group>"; };
+ CA55CD3E202C16AA00CE7095 /* MGLCameraChange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChange.h; sourceTree = "<group>"; };
DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = "<group>"; };
DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = "<group>"; };
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; };
@@ -1449,19 +1455,21 @@
DA8848331CBAFB2A00AB86E3 /* Kit */ = {
isa = PBXGroup;
children = (
- 355ADFF91E9281C300F3939D /* Views */,
- 35CE617F1D4165C2004F2359 /* Categories */,
DAD165841CF4D06B001FF4B9 /* Annotations */,
+ 35CE617F1D4165C2004F2359 /* Categories */,
+ DA88487F1CBB033F00AB86E3 /* Fabric */,
+ DA8848881CBB036000AB86E3 /* SMCalloutView */,
DAD165851CF4D08B001FF4B9 /* Telemetry */,
+ 355ADFF91E9281C300F3939D /* Views */,
+ CA55CD3E202C16AA00CE7095 /* MGLCameraChange.h */,
+ CA55CD3D202C16A900CE7095 /* MGLCameraChange.m */,
DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */,
DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */,
- DA8848361CBAFB8500AB86E3 /* MGLMapView.h */,
DA17BE2F1CC4BAC300402C41 /* MGLMapView_Private.h */,
+ DA8848361CBAFB8500AB86E3 /* MGLMapView.h */,
+ DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */,
DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */,
DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */,
- DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */,
- DA88487F1CBB033F00AB86E3 /* Fabric */,
- DA8848881CBB036000AB86E3 /* SMCalloutView */,
);
name = Kit;
path = src;
@@ -1764,6 +1772,7 @@
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */,
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */,
+ CA55CD41202C16AA00CE7095 /* MGLCameraChange.h in Headers */,
1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */,
DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */,
35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */,
@@ -1896,6 +1905,7 @@
3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */,
35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */,
+ CA55CD42202C16AA00CE7095 /* MGLCameraChange.h in Headers */,
DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */,
DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */,
927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */,
@@ -2407,6 +2417,7 @@
35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
+ CA55CD3F202C16AA00CE7095 /* MGLCameraChange.m in Sources */,
DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */,
3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */,
@@ -2497,6 +2508,7 @@
35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
+ CA55CD40202C16AA00CE7095 /* MGLCameraChange.m in Sources */,
DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */,
3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */,
diff --git a/platform/ios/src/MGLCameraChange.h b/platform/ios/src/MGLCameraChange.h
new file mode 100644
index 0000000000..bfd956d66d
--- /dev/null
+++ b/platform/ios/src/MGLCameraChange.h
@@ -0,0 +1,14 @@
+#import "MGLFoundation.h"
+
+typedef NSString *MGLCameraChangeReason NS_TYPED_ENUM;
+
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonUnknown;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonProgramatic;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGestureResetNorth;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGesturePan;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGesturePinch;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGestureRotate;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGestureDoubleTap;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGestureTwoFingerSingleTap;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGestureQuickZoom;
+extern MGL_EXPORT const MGLCameraChangeReason MGLCameraChangeReasonGesturePitchStart;
diff --git a/platform/ios/src/MGLCameraChange.m b/platform/ios/src/MGLCameraChange.m
new file mode 100644
index 0000000000..02f00fc990
--- /dev/null
+++ b/platform/ios/src/MGLCameraChange.m
@@ -0,0 +1,16 @@
+#import "MGLCameraChange.h"
+
+#define MGL_CAMERA_CHANGE_REASON_DEF(name) const MGLCameraChangeReason name = @ #name
+
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonUnknown);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonProgramatic);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGestureResetNorth);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGesturePan);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGesturePinch);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGestureRotate);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGestureDoubleTap);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGestureTwoFingerSingleTap);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGestureQuickZoom);
+MGL_CAMERA_CHANGE_REASON_DEF(MGLCameraChangeReasonGesturePitchStart);
+
+#undef MGL_CAMERA_CHANGE_REASON_DEF
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 84afc56223..dae9988c79 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 cameraChangeReason;
+
/// Mapping from reusable identifiers to annotation images.
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
@@ -535,13 +537,14 @@ public:
[[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(sleepGL:) name:UIApplicationWillResignActiveNotification object:nil]; // $$jr missing???
[[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;
@@ -549,6 +552,9 @@ public:
mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
options.padding = padding;
options.zoom = 0;
+
+ _cameraChangeReason = MGLCameraChangeReasonUnknown;
+
_mbglMap->jumpTo(options);
_pendingLatitude = NAN;
_pendingLongitude = NAN;
@@ -1235,6 +1241,8 @@ public:
- (void)handleCompassTapGesture:(__unused id)sender
{
+ self.cameraChangeReason = MGLCameraChangeReasonGestureResetNorth;
+
[self resetNorthAnimated:YES];
if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading ||
@@ -1257,6 +1265,7 @@ public:
- (void)notifyGestureDidBegin {
BOOL animated = NO;
+
[self cameraWillChangeAnimated:animated];
_mbglMap->setGestureInProgress(true);
_changeDelimiterSuppressionDepth++;
@@ -1280,6 +1289,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.cameraChangeReason];
+ }
+ 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;
@@ -1287,7 +1313,9 @@ public:
_mbglMap->cancelTransitions();
MGLMapCamera *oldCamera = self.camera;
-
+
+ self.cameraChangeReason = MGLCameraChangeReasonGesturePan;
+
if (pan.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
@@ -1301,9 +1329,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];
@@ -1325,9 +1352,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));
}
@@ -1358,6 +1384,8 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:pinch];
MGLMapCamera *oldCamera = self.camera;
+ self.cameraChangeReason = MGLCameraChangeReasonGesturePinch;
+
if (pinch.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
@@ -1374,9 +1402,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
@@ -1428,9 +1455,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 {
@@ -1456,7 +1482,9 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:rotate];
MGLMapCamera *oldCamera = self.camera;
-
+
+ self.cameraChangeReason = MGLCameraChangeReasonGestureRotate;
+
if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
@@ -1483,9 +1511,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 });
}
@@ -1503,9 +1530,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));
@@ -1550,6 +1576,8 @@ public:
}
[self deselectAnnotation:self.selectedAnnotation animated:YES];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement);
+
+ // Should we call mapView:didSingleTapAtCoordinate: here? If so, what coordinate?
return;
}
@@ -1560,10 +1588,21 @@ 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];
}
+ else
+ {
+ // An annotation wasn't selected or deselected.
+ if ([self.delegate respondsToSelector:@selector(mapView:didSingleTapAtCoordinate:)])
+ {
+ CGPoint pointInView = [singleTap locationInView:singleTap.view];
+ CLLocationCoordinate2D tapCoordinate = [self convertPoint:pointInView toCoordinateFromView:singleTap.view];
+
+ [self.delegate mapView:self didSingleTapAtCoordinate:tapCoordinate];
+ }
+ }
}
/**
@@ -1642,6 +1681,8 @@ public:
if (doubleTap.state == UIGestureRecognizerStateEnded)
{
+ self.cameraChangeReason = MGLCameraChangeReasonGestureDoubleTap;
+
MGLMapCamera *oldCamera = self.camera;
double newZoom = round(self.zoomLevel) + 1.0;
@@ -1649,9 +1690,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];
@@ -1678,9 +1718,13 @@ public:
_mbglMap->cancelTransitions();
+ self.cameraChangeReason = MGLCameraChangeReasonGestureTwoFingerSingleTap;
+
if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
+
+ [self notifyGestureDidBegin];
}
else if (twoFingerTap.state == UIGestureRecognizerStateEnded)
{
@@ -1691,9 +1735,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));
@@ -1713,7 +1756,9 @@ public:
if ( ! self.isZoomEnabled) return;
_mbglMap->cancelTransitions();
-
+
+ self.cameraChangeReason = MGLCameraChangeReasonGestureQuickZoom;
+
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
@@ -1736,9 +1781,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 });
}
@@ -1758,6 +1802,8 @@ public:
_mbglMap->cancelTransitions();
+ self.cameraChangeReason = MGLCameraChangeReasonGesturePitchStart;
+
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
@@ -1777,8 +1823,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 });
}
@@ -1936,7 +1981,8 @@ public:
{
id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO];
if(!annotation) {
- return NO;
+ // Only allow this gesture to be recognized if the delegate implements the single tap method
+ return [self.delegate respondsToSelector:@selector(mapView:didSingleTapAtCoordinate:)];
}
}
}
@@ -2854,6 +2900,8 @@ public:
{
self.userTrackingMode = MGLUserTrackingModeNone;
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
[self _setCenterCoordinate:centerCoordinate edgePadding:self.contentInset zoomLevel:zoomLevel direction:direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil completionHandler:completion];
}
@@ -2903,6 +2951,9 @@ public:
}
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
_mbglMap->easeTo(cameraOptions, animationOptions);
}
@@ -2926,6 +2977,8 @@ public:
if (zoomLevel == self.zoomLevel) return;
_mbglMap->cancelTransitions();
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
CGFloat duration = animated ? MGLAnimationDuration : 0;
_mbglMap->setZoom(zoomLevel,
@@ -3014,6 +3067,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.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
[self _setVisibleCoordinates:coordinates count:count edgePadding:insets direction:direction duration:duration animationTimingFunction:function completionHandler:completion];
}
@@ -3063,6 +3119,9 @@ public:
[self willChangeValueForKey:@"visibleCoordinateBounds"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"visibleCoordinateBounds"];
}
@@ -3096,6 +3155,8 @@ public:
CGFloat duration = animated ? MGLAnimationDuration : 0;
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
if (self.userTrackingMode == MGLUserTrackingModeNone)
{
_mbglMap->setBearing(direction,
@@ -3180,6 +3241,9 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
@@ -3236,6 +3300,9 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReason = MGLCameraChangeReasonProgramatic;
+
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets];
_mbglMap->flyTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
@@ -3396,6 +3463,26 @@ public:
return [self metersPerPointAtLatitude:latitude];
}
+#pragma mark - Camera Change Reason -
+
+- (void)resetCameraChangeReason
+{
+ self.cameraChangeReason = MGLCameraChangeReasonUnknown;
+}
+
+- (void)setCameraChangeReason:(MGLCameraChangeReason)reason
+{
+ // If not already set, update. This is required since the reason can be set in succession (for
+ // example "resetting north" sets the reason to MGLCameraChangeReasonGestureResetNorth before
+ // calling a public method which calls self.cameraChangeReason with MGLCameraChangeReasonProgramatic.
+ // We want the first reason to win out. This may be better handled with a stack or some other
+ // mechanism.
+ if (MGLCameraChangeReasonUnknown == _cameraChangeReason)
+ {
+ _cameraChangeReason = reason;
+ }
+}
+
#pragma mark - Styling -
- (NS_ARRAY_OF(NSURL *) *)bundledStyleURLs
@@ -5342,9 +5429,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:regionWillChangeAnimated:reason:)])
+ {
+ [self.delegate mapView:self regionWillChangeAnimated:animated reason:self.cameraChangeReason];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)])
+ {
+ [self.delegate mapView:self regionWillChangeAnimated:animated];
+ }
}
}
@@ -5358,8 +5452,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.cameraChangeReason];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)])
{
[self.delegate mapViewRegionIsChanging:self];
}
@@ -5372,9 +5470,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:regionDidChangeAnimated:reason:)];
+
+ if ((respondsToSelector || respondsToSelectorWithReason) &&
+ ([UIApplication sharedApplication].applicationState == UIApplicationStateActive))
{
_featureAccessibilityElements = nil;
_visiblePlaceFeatures = nil;
@@ -5386,7 +5488,17 @@ public:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
}
- [self.delegate mapView:self regionDidChangeAnimated:animated];
+
+ if (respondsToSelectorWithReason)
+ {
+ [self.delegate mapView:self regionDidChangeAnimated:animated reason:self.cameraChangeReason];
+ }
+ else
+ {
+ [self.delegate mapView:self regionDidChangeAnimated:animated];
+ }
+
+ [self resetCameraChangeReason];
}
}
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 096711fcbb..dd5eb68d88 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -1,6 +1,7 @@
#import <UIKit/UIKit.h>
#import "MGLTypes.h"
+#import "MGLCameraChange.h"
NS_ASSUME_NONNULL_BEGIN
@@ -29,10 +30,26 @@ NS_ASSUME_NONNULL_BEGIN
@param mapView The map view whose viewpoint will change.
@param animated Whether the change will cause an animated effect on the map.
+
+ @note If `mapView:regionWillChangeAnimated:reason` is implemented this method will not be called.
*/
- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
/**
+ Tells the delegate that the viewpoint depicted by the map view is about to change.
+
+ This method is called whenever the currently displayed map camera will start
+ changing for any reason.
+
+ @param mapView The map view whose viewpoint will change.
+ @param animated Whether the change will cause an animated effect on the map.
+ @param reason The reason for the camera change.
+
+ @note If this method is implemented `mapView:regionWillChangeAnimated:` will not be called.
+ */
+- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated reason:(MGLCameraChangeReason)reason;
+
+/**
Tells the delegate that the viewpoint depicted by the map view is changing.
This method is called as the currently displayed map camera changes as part of
@@ -45,10 +62,31 @@ NS_ASSUME_NONNULL_BEGIN
as possible to avoid affecting performance.
@param mapView The map view whose viewpoint is changing.
+
+ @note If `mapView:regionIsChangingWithReason:` is implemented this method will not be called.
*/
- (void)mapViewRegionIsChanging:(MGLMapView *)mapView;
/**
+ Tells the delegate that the viewpoint depicted by the map view is changing.
+
+ This method is called as the currently displayed map camera changes as part of
+ an animation, whether due to a user gesture or due to a call to a method such
+ as `-[MGLMapView setCamera:animated:]`. This method can be called before
+ `-mapViewDidFinishLoadingMap:` is called.
+
+ During the animation, this method may be called many times to report updates to
+ the viewpoint. Therefore, your implementation of this method should be as lightweight
+ as possible to avoid affecting performance.
+
+ @param mapView The map view whose viewpoint is changing.
+ @param reason The reason for the camera change.
+
+ @note If this method is implemented `mapViewRegionIsChanging:` will not be called.
+ */
+- (void)mapView:(MGLMapView *)mapView regionIsChangingWithReason:(MGLCameraChangeReason)reason;
+
+/**
Tells the delegate that the viewpoint depicted by the map view has finished
changing.
@@ -58,10 +96,28 @@ NS_ASSUME_NONNULL_BEGIN
@param mapView The map view whose viewpoint has changed.
@param animated Whether the change caused an animated effect on the map.
+
+ @note If `mapView:regionDidChangeAnimated:reason:` is implemented this method will not be called.
*/
- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
/**
+ Tells the delegate that the viewpoint depicted by the map view has finished
+ changing.
+
+ This method is called whenever the currently displayed map camera has finished
+ changing, after any calls to `-mapViewRegionIsChanging:` due to animation. Therefore,
+ this method can be called before `-mapViewDidFinishLoadingMap:` is called.
+
+ @param mapView The map view whose viewpoint has changed.
+ @param animated Whether the change caused an animated effect on the map.
+ @param reason The reason for the camera change.
+
+ @note If this method is implemented `mapView:regionDidChangeAnimated:` will not be called.
+ */
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated reason:(MGLCameraChangeReason)reason;
+
+/**
Asks the delegate whether the map view should be allowed to change from the
existing camera to the new camera in response to a user gesture.
@@ -80,9 +136,50 @@ NS_ASSUME_NONNULL_BEGIN
method returns `YES`, this camera becomes the map view’s camera.
@return A Boolean value indicating whether the map view should stay at
`oldCamera` or change to `newCamera`.
+
+ @note If `mapView:shouldChangeFromCamera:toCamera:reason:` is implemented this method will not be called.
*/
- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera;
+/**
+ Asks the delegate whether the map view should be allowed to change from the
+ existing camera to the new camera in response to a user gesture.
+
+ This method is called as soon as the user gesture is recognized. It is not
+ called in response to a programmatic camera change, such as by setting the
+ `centerCoordinate` property or calling `-flyToCamera:completionHandler:`.
+
+ This method is called many times during gesturing, so you should avoid performing
+ complex or performance-intensive tasks in your implementation.
+
+ @param mapView The map view that the user is manipulating.
+ @param oldCamera The camera representing the viewpoint at the moment the
+ gesture is recognized. If this method returns `NO`, the map view’s camera
+ continues to be this camera.
+ @param newCamera The expected camera after the gesture completes. If this
+ method returns `YES`, this camera becomes the map view’s camera.
+ @param reason The reason for the camera change.
+ @return A Boolean value indicating whether the map view should stay at
+ `oldCamera` or change to `newCamera`.
+
+ @note If this method is implemented `mapView:shouldChangeFromCamera:toCamera:` will not be called.
+ */
+- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera reason:(MGLCameraChangeReason)reason;
+
+#pragma mark Responding to user gestures
+
+/**
+ Tells the delegate that the user has tapped on the map view.
+
+ @param mapView The map view that was tapped.
+ @param coordinate Location of tap in world coordinates.
+
+ @note This method will not be called when the user single taps on an annotation, or if an annotation
+ is currently selected.
+ */
+- (void)mapView:(MGLMapView *)mapView didSingleTapAtCoordinate:(CLLocationCoordinate2D)coordinate;
+
+
#pragma mark Loading the Map
/**
diff --git a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
index 50f101e86b..875b4a34da 100644
--- a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
+++ b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
@@ -11,7 +11,7 @@ class MGLMapViewDelegateIntegrationTests: XCTestCase {
extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
- func mapViewRegionIsChanging(_ mapView: MGLMapView) {}
+ func mapViewRegionIsChanging(_ mapView: MGLMapView, reason: MGLCameraChangeReason) {}
func mapView(_ mapView: MGLMapView, didChange mode: MGLUserTrackingMode, animated: Bool) {}
@@ -33,9 +33,9 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, didDeselect annotation: MGLAnnotation) {}
- func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool) {}
+ func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool, reason: MGLCameraChangeReason) {}
- func mapView(_ mapView: MGLMapView, regionWillChangeAnimated animated: Bool) {}
+ func mapView(_ mapView: MGLMapView, regionWillChangeAnimated animated: Bool, reason: MGLCameraChangeReason) {}
func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {}
diff --git a/platform/ios/uitest/MapViewTests.m b/platform/ios/uitest/MapViewTests.m
index 4ed3d89399..8a5521a86e 100644
--- a/platform/ios/uitest/MapViewTests.m
+++ b/platform/ios/uitest/MapViewTests.m
@@ -538,10 +538,13 @@
userInfo:@{ @"animated" : @(animated) }];
}
-- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated reason:(MGLCameraChangeReason)reason {
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"regionDidChangeAnimated"
object:mapView
- userInfo:@{ @"animated" : @(animated) }];
+ userInfo:@{ @"animated" : @(animated),
+ @"reason" : @(reason)
+ }];
}
- (void)testDelegatesStartStopLocatingUser {