diff options
author | Jordan Kiley <jmkiley@users.noreply.github.com> | 2019-08-20 22:12:08 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-20 22:12:08 -0700 |
commit | 6f78560f249ef057f95d16fb1f55de6d97295681 (patch) | |
tree | 88b6e22d176dba3b8ccc7f48bfe134cb25bb8b5e /platform/ios/src | |
parent | 2f43a7b87b7d12298318cb6611ac9a7ef8868b09 (diff) | |
download | qtlocation-mapboxgl-6f78560f249ef057f95d16fb1f55de6d97295681.tar.gz |
[ios] Add threshold for triggering the rotate gesture recognizer (#14929)
Diffstat (limited to 'platform/ios/src')
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 147 |
1 files changed, 135 insertions, 12 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index d77f94d8ba..51102233cf 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -128,6 +128,9 @@ const CLLocationDirection MGLToleranceForSnappingToNorth = 7; /// Distance threshold to stop the camera while animating. const CLLocationDistance MGLDistanceThresholdForCameraPause = 500; +/// Rotation threshold while a pinch gesture is occurring. +static NSString * const MGLRotationThresholdWhileZoomingKey = @"MGLRotationThresholdWhileZooming"; + /// Reuse identifier and file name of the default point annotation image. static NSString * const MGLDefaultStyleMarkerSymbolName = @"default_marker"; @@ -240,6 +243,10 @@ public: @property (nonatomic) CGFloat quickZoomStart; @property (nonatomic, getter=isDormant) BOOL dormant; @property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed; +@property (nonatomic) CGFloat rotationThresholdWhileZooming; +@property (nonatomic) CGFloat rotationBeforeThresholdMet; +@property (nonatomic) BOOL isZooming; +@property (nonatomic) BOOL isRotating; @property (nonatomic) BOOL shouldTriggerHapticFeedbackForCompass; @property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement; @property (nonatomic) MGLAnnotationContainerView *annotationContainerView; @@ -1622,6 +1629,9 @@ public: { self.scale = powf(2, [self zoomLevel]); + if (abs(pinch.velocity) > abs(self.rotate.velocity)) { + self.isZooming = YES; + } [self notifyGestureDidBegin]; } else if (pinch.state == UIGestureRecognizerStateChanged) @@ -1697,6 +1707,7 @@ public: } } + self.isZooming = NO; [self notifyGestureDidEndWithDrift:drift]; [self unrotateIfNeededForGesture]; } @@ -1709,6 +1720,106 @@ public: { if ( ! self.isRotateEnabled) return; + if ([[NSUserDefaults standardUserDefaults] objectForKey:MGLRotationThresholdWhileZoomingKey]) { + [self handleRotateGestureRecognizerWithThreshold:rotate]; + } else { + [self cancelTransitions]; + + CGPoint centerPoint = [self anchorPointForGesture:rotate]; + MGLMapCamera *oldCamera = self.camera; + + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureRotate; + + if (rotate.state == UIGestureRecognizerStateBegan) + { + self.angle = MGLRadiansFromDegrees(*self.mbglMap.getCameraOptions().bearing) * -1; + + self.isRotating = YES; + if (self.userTrackingMode != MGLUserTrackingModeNone) + { + self.userTrackingMode = MGLUserTrackingModeFollow; + } + + self.shouldTriggerHapticFeedbackForCompass = NO; + [self notifyGestureDidBegin]; + } + if (rotate.state == UIGestureRecognizerStateChanged) + { + CGFloat newDegrees = MGLDegreesFromRadians(self.angle + rotate.rotation) * -1; + + // constrain to +/-30 degrees when merely rotating like Apple does + // + if ( ! self.isRotationAllowed && std::abs(self.pinch.scale) < 10) + { + newDegrees = fminf(newDegrees, 30); + newDegrees = fmaxf(newDegrees, -30); + } + + MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint]; + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) + { + self.mbglMap.jumpTo(mbgl::CameraOptions() + .withBearing(newDegrees) + .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y})); + } + + [self cameraIsChanging]; + + // Trigger a light haptic feedback event when the user rotates to due north. + if (@available(iOS 10.0, *)) + { + if (self.isHapticFeedbackEnabled && fabs(newDegrees) <= 1 && self.shouldTriggerHapticFeedbackForCompass) + { + UIImpactFeedbackGenerator *hapticFeedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight]; + [hapticFeedback impactOccurred]; + + self.shouldTriggerHapticFeedbackForCompass = NO; + } + else if (fabs(newDegrees) > 1) + { + self.shouldTriggerHapticFeedbackForCompass = YES; + } + } + } + else if ((rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)) + { + CGFloat velocity = rotate.velocity; + CGFloat decelerationRate = self.decelerationRate; + if (decelerationRate != MGLMapViewDecelerationRateImmediate && fabs(velocity) > 3) + { + CGFloat radians = self.angle + rotate.rotation; + CGFloat newRadians = radians + velocity * decelerationRate * 0.1; + CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1; + + MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint]; + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) + { + self.mbglMap.easeTo(mbgl::CameraOptions() + .withBearing(newDegrees) + .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }), + MGLDurationFromTimeInterval(decelerationRate)); + + [self notifyGestureDidEndWithDrift:YES]; + __weak MGLMapView *weakSelf = self; + + [self animateWithDelay:decelerationRate animations:^ + { + [weakSelf unrotateIfNeededForGesture]; + }]; + } + } + else + { + [self notifyGestureDidEndWithDrift:NO]; + [self unrotateIfNeededForGesture]; + } + } + } +} + +- (void)handleRotateGestureRecognizerWithThreshold:(UIRotationGestureRecognizer *)rotate { [self cancelTransitions]; CGPoint centerPoint = [self anchorPointForGesture:rotate]; @@ -1716,20 +1827,29 @@ public: self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureRotate; - if (rotate.state == UIGestureRecognizerStateBegan) + _rotationThresholdWhileZooming = [[[NSUserDefaults standardUserDefaults] objectForKey:MGLRotationThresholdWhileZoomingKey] floatValue]; + + // Check whether a zoom triggered by a pinch gesture is occurring and if the rotation threshold has been met. + if (MGLDegreesFromRadians(self.rotationBeforeThresholdMet) < self.rotationThresholdWhileZooming && self.isZooming && !self.isRotating) { + self.rotationBeforeThresholdMet += fabs(rotate.rotation); + rotate.rotation = 0; + return; + } + + if (rotate.state == UIGestureRecognizerStateBegan || ! self.isRotating) { self.angle = MGLRadiansFromDegrees(*self.mbglMap.getCameraOptions().bearing) * -1; + self.isRotating = YES; if (self.userTrackingMode != MGLUserTrackingModeNone) { self.userTrackingMode = MGLUserTrackingModeFollow; } self.shouldTriggerHapticFeedbackForCompass = NO; - [self notifyGestureDidBegin]; } - else if (rotate.state == UIGestureRecognizerStateChanged) + if (rotate.state == UIGestureRecognizerStateChanged) { CGFloat newDegrees = MGLDegreesFromRadians(self.angle + rotate.rotation) * -1; @@ -1740,14 +1860,14 @@ public: newDegrees = fminf(newDegrees, 30); newDegrees = fmaxf(newDegrees, -30); } - + MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint]; if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { - self.mbglMap.jumpTo(mbgl::CameraOptions() - .withBearing(newDegrees) - .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y})); + self.mbglMap.jumpTo(mbgl::CameraOptions() + .withBearing(newDegrees) + .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y})); } [self cameraIsChanging]; @@ -1768,8 +1888,12 @@ public: } } } - else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled) + else if ((rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)) { + self.rotationBeforeThresholdMet = 0; + if (! self.isRotating) { return; } + self.isRotating = NO; + CGFloat velocity = rotate.velocity; CGFloat decelerationRate = self.decelerationRate; if (decelerationRate != MGLMapViewDecelerationRateImmediate && fabs(velocity) > 3) @@ -1783,14 +1907,13 @@ public: if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { self.mbglMap.easeTo(mbgl::CameraOptions() - .withBearing(newDegrees) - .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }), + .withBearing(newDegrees) + .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }), MGLDurationFromTimeInterval(decelerationRate)); [self notifyGestureDidEndWithDrift:YES]; - __weak MGLMapView *weakSelf = self; - + [self animateWithDelay:decelerationRate animations:^ { [weakSelf unrotateIfNeededForGesture]; |