From 4b85fa6f11d852b1c6be1f0464abfa3ae34b565d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 5 Jul 2016 16:20:00 -0700 Subject: [PATCH] [ios, macos] Added delegate method to restrict movement Added a way for the delegate to restrict where the user can move within the map using gestures. Fixes #2457. --- platform/ios/CHANGELOG.md | 11 +++++---- platform/ios/src/MGLMapView.mm | 10 ++++++++ platform/ios/src/MGLMapViewDelegate.h | 19 +++++++++++++++ platform/macos/CHANGELOG.md | 8 +++++-- platform/macos/src/MGLMapView.mm | 32 +++++++++++++++++++++++++ platform/macos/src/MGLMapViewDelegate.h | 19 +++++++++++++++ 6 files changed, 92 insertions(+), 7 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 1140753ebbe..98382f9d539 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -99,6 +99,12 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue that caused an assertion failure if a `MGLShapeCollection` (a GeoJSON GeometryCollection) was created with an empty array of shapes. ([#7632](https://github.com/mapbox/mapbox-gl-native/pull/7632)) * Improved the precision of annotations at zoom levels greater than 18. ([#5517](https://github.com/mapbox/mapbox-gl-native/pull/5517)) +### User interactivity + +* Added a method to MGLMapViewDelegate, `-mapView:shouldChangeFromCamera:toCamera:`, that you can implement to restrict which parts the user can navigate to using gestures. ([#5584](https://github.com/mapbox/mapbox-gl-native/pull/5584)) +* 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)) +* As the user zooms in, tiles from lower zoom levels are scaled up until tiles for higher zoom levels are loaded. ([#5143](https://github.com/mapbox/mapbox-gl-native/pull/5143)) + ### Networking and offline maps * Fixed an issue preventing an MGLMapView from loading tiles while an offline pack is downloading. ([#6446](https://github.com/mapbox/mapbox-gl-native/pull/6446)) @@ -112,14 +118,9 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Other changes -* Raster tiles such as those from Mapbox Satellite are now cached, eliminating flashing while panning back and forth. ([#7091](https://github.com/mapbox/mapbox-gl-native/pull/7091)) -* Improved the performance of symbol style layers. ([#7025](https://github.com/mapbox/mapbox-gl-native/pull/7025)) -* As the user zooms in, tiles from lower zoom levels are scaled up until tiles for higher zoom levels are loaded. ([#5143](https://github.com/mapbox/mapbox-gl-native/pull/5143)) * 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)) -* Fixed issues related to the visibility of sources in viewports less than 512 pixels wide or tall. ([#7438](https://github.com/mapbox/mapbox-gl-native/pull/7438)) ## 3.3.7 - November 17, 2016 diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 35ad5830816..3569b447ad9 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1201,6 +1201,8 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)pan _mbglMap->cancelTransitions(); + MGLMapCamera *oldCamera = self.camera; + BOOL didChangeCamera = NO; if (pan.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan]; @@ -1214,6 +1216,7 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)pan CGPoint delta = [pan translationInView:pan.view]; _mbglMap->moveBy({ delta.x, delta.y }); [pan setTranslation:CGPointZero inView:pan.view]; + didChangeCamera = YES; [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; } @@ -1246,6 +1249,13 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)pan MGLEventKeyZoomLevel: @(zoom) }]; } + + if (didChangeCamera + && [self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] + && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:self.camera]) + { + self.camera = oldCamera; + } } - (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index 8a57e15f7a4..198cd893af5 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -59,6 +59,25 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated; +/** + 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:`. + + @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. + @return A Boolean value indicating whether the map view should stay at + `oldCamera` or change to `newCamera`. + */ +- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera; + #pragma mark Loading the Map /** diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index f4a4607c339..d6865b43954 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -67,6 +67,7 @@ This version of the Mapbox macOS SDK corresponds to version 3.4.0 of the Mapbox * Improved style parsing performance. ([#6170](https://github.com/mapbox/mapbox-gl-native/pull/6170)) * Improved feature querying performance. ([#6514](https://github.com/mapbox/mapbox-gl-native/pull/6514)) * Fixed an issue where shapes that cannot currently be visually represented as annotations were still shown on the map as point annotations. ([#6764](https://github.com/mapbox/mapbox-gl-native/issues/6764)) +* Raster tiles such as those from Mapbox Satellite are now cached, eliminating flashing while panning back and forth. ([#7091](https://github.com/mapbox/mapbox-gl-native/pull/7091)) ### Annotations @@ -81,6 +82,11 @@ This version of the Mapbox macOS SDK corresponds to version 3.4.0 of the Mapbox * Fixed an issue that caused an assertion failure if a `MGLShapeCollection` (a GeoJSON GeometryCollection) was created with an empty array of shapes. ([#7632](https://github.com/mapbox/mapbox-gl-native/pull/7632)) * Improved the precision of annotations at zoom levels greater than 18. ([#5517](https://github.com/mapbox/mapbox-gl-native/pull/5517)) +### User interactivity + +* Added a method to MGLMapViewDelegate, `-mapView:shouldChangeFromCamera:toCamera:`, that you can implement to restrict which parts the user can navigate to using gestures. ([#5584](https://github.com/mapbox/mapbox-gl-native/pull/5584)) +* 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)) + ### Networking and offline maps * Fixed an issue preventing an MGLMapView from loading tiles while an offline pack is downloading. ([#6446](https://github.com/mapbox/mapbox-gl-native/pull/6446)) @@ -94,11 +100,9 @@ This version of the Mapbox macOS SDK corresponds to version 3.4.0 of the Mapbox ### Other changes -* Raster tiles such as those from Mapbox Satellite are now cached, eliminating flashing while panning back and forth. ([#7091](https://github.com/mapbox/mapbox-gl-native/pull/7091)) * 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 diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 86d74d768d3..e23a89db1cd 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -988,8 +988,13 @@ - (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate animated:(B - (void)offsetCenterCoordinateBy:(NSPoint)delta animated:(BOOL)animated { [self willChangeValueForKey:@"centerCoordinate"]; _mbglMap->cancelTransitions(); + MGLMapCamera *oldCamera = self.camera; _mbglMap->moveBy({ delta.x, delta.y }, MGLDurationInSecondsFromTimeInterval(animated ? MGLAnimationDuration : 0)); + if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] + && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:self.camera]) { + self.camera = oldCamera; + } [self didChangeValueForKey:@"centerCoordinate"]; } @@ -1036,8 +1041,13 @@ - (void)zoomBy:(double)zoomDelta animated:(BOOL)animated { - (void)scaleBy:(double)scaleFactor atPoint:(NSPoint)point animated:(BOOL)animated { [self willChangeValueForKey:@"centerCoordinate"]; [self willChangeValueForKey:@"zoomLevel"]; + MGLMapCamera *oldCamera = self.camera; mbgl::ScreenCoordinate center(point.x, self.bounds.size.height - point.y); _mbglMap->scaleBy(scaleFactor, center, MGLDurationInSecondsFromTimeInterval(animated ? MGLAnimationDuration : 0)); + if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] + && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:self.camera]) { + self.camera = oldCamera; + } [self didChangeValueForKey:@"zoomLevel"]; [self didChangeValueForKey:@"centerCoordinate"]; } @@ -1372,15 +1382,25 @@ - (void)handlePanGesture:(NSPanGestureRecognizer *)gestureRecognizer { _directionAtBeginningOfGesture = self.direction; _pitchAtBeginningOfGesture = _mbglMap->getPitch(); } else if (gestureRecognizer.state == NSGestureRecognizerStateChanged) { + MGLMapCamera *oldCamera = self.camera; + BOOL didChangeCamera = NO; mbgl::ScreenCoordinate center(startPoint.x, self.bounds.size.height - startPoint.y); if (self.rotateEnabled) { CLLocationDirection newDirection = _directionAtBeginningOfGesture - delta.x / 10; [self willChangeValueForKey:@"direction"]; _mbglMap->setBearing(newDirection, center); + didChangeCamera = YES; [self didChangeValueForKey:@"direction"]; } if (self.pitchEnabled) { _mbglMap->setPitch(_pitchAtBeginningOfGesture + delta.y / 5, center); + didChangeCamera = YES; + } + + if (didChangeCamera + && [self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] + && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:self.camera]) { + self.camera = oldCamera; } } } else if (self.scrollEnabled) { @@ -1420,7 +1440,12 @@ - (void)handleMagnificationGesture:(NSMagnificationGestureRecognizer *)gestureRe if (gestureRecognizer.magnification > -1) { [self willChangeValueForKey:@"zoomLevel"]; [self willChangeValueForKey:@"centerCoordinate"]; + MGLMapCamera *oldCamera = self.camera; _mbglMap->setScale(_scaleAtBeginningOfGesture * (1 + gestureRecognizer.magnification), center); + if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] + && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:self.camera]) { + self.camera = oldCamera; + } [self didChangeValueForKey:@"centerCoordinate"]; [self didChangeValueForKey:@"zoomLevel"]; } @@ -1495,9 +1520,16 @@ - (void)handleRotationGesture:(NSRotationGestureRecognizer *)gestureRecognizer { _mbglMap->setGestureInProgress(true); _directionAtBeginningOfGesture = self.direction; } else if (gestureRecognizer.state == NSGestureRecognizerStateChanged) { + MGLMapCamera *oldCamera = self.camera; + NSPoint rotationPoint = [gestureRecognizer locationInView:self]; mbgl::ScreenCoordinate center(rotationPoint.x, self.bounds.size.height - rotationPoint.y); _mbglMap->setBearing(_directionAtBeginningOfGesture + gestureRecognizer.rotationInDegrees, center); + + if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] + && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:self.camera]) { + self.camera = oldCamera; + } } else if (gestureRecognizer.state == NSGestureRecognizerStateEnded || gestureRecognizer.state == NSGestureRecognizerStateCancelled) { _mbglMap->setGestureInProgress(false); diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h index 534e28e3a89..e212292b6be 100644 --- a/platform/macos/src/MGLMapViewDelegate.h +++ b/platform/macos/src/MGLMapViewDelegate.h @@ -61,6 +61,25 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)mapView:(MGLMapView *)mapView cameraDidChangeAnimated:(BOOL)animated; +/** + 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:`. + + @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. + @return A Boolean value indicating whether the map view should stay at + `oldCamera` or change to `newCamera`. + */ +- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera; + #pragma mark Loading the Map /**