diff options
Diffstat (limited to 'platform/ios')
-rw-r--r-- | platform/ios/CHANGELOG.md | 4 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 93 | ||||
-rw-r--r-- | platform/ios/test/MGLMapViewPitchTests.m | 39 |
3 files changed, 100 insertions, 36 deletions
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 9b0c6ae2fb..5dc73de6d3 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## master + +* Fixed an issue that caused the tilt gesture to trigger too easily and conflict with pinch or pan gestures. ([#15349](https://github.com/mapbox/mapbox-gl-native/pull/15349)) + ## 5.3.0 This release changes how offline tile requests are billed — they are now billed on a pay-as-you-go basis and all developers are able raise the offline tile limit for their users. Offline requests were previously exempt from monthly active user (MAU) billing and increasing the offline per-user tile limit to more than 6,000 tiles required the purchase of an enterprise license. By upgrading to this release, you are opting into the changes outlined in [this blog post](https://blog.mapbox.com/offline-maps-for-all-bb0fc51827be) and [#15380](https://github.com/mapbox/mapbox-gl-native/pull/15380). diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index d242feb38d..3fc692f4e0 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -157,6 +157,9 @@ static const NSUInteger MGLPresentsWithTransactionAnnotationCount = 0; /// An indication that the requested annotation was not found or is nonexistent. enum { MGLAnnotationTagNotFound = UINT32_MAX }; +/// The threshold used to consider when a tilt gesture should start. +const CLLocationDegrees MGLHorizontalTiltToleranceDegrees = 45.0; + /// Mapping from an annotation tag to metadata about that annotation, including /// the annotation itself. typedef std::unordered_map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationTagContextMap; @@ -267,6 +270,9 @@ public: @property (nonatomic) MGLMapDebugMaskOptions residualDebugMask; @property (nonatomic, copy) NSURL *residualStyleURL; +/// Tilt gesture recognizer helper +@property (nonatomic, assign) CGPoint dragGestureMiddlePoint; + - (mbgl::Map &)mbglMap; @end @@ -2139,6 +2145,14 @@ public: if (twoFingerDrag.state == UIGestureRecognizerStateBegan) { + CGPoint midPoint = [twoFingerDrag translationInView:twoFingerDrag.view]; + // In the following if and for the first execution middlePoint + // will be equal to dragGestureMiddlePoint and the resulting + // gestureSlopeAngle will be 0º causing a small delay, + // initializing dragGestureMiddlePoint with the current midPoint + // but substracting one point from 'y' forces an initial 90º angle + // making the gesture avoid the delay + self.dragGestureMiddlePoint = CGPointMake(midPoint.x, midPoint.y-1); initialPitch = *self.mbglMap.getCameraOptions().pitch; [self notifyGestureDidBegin]; } @@ -2150,30 +2164,45 @@ public: twoFingerDrag.state = UIGestureRecognizerStateEnded; return; } - - CGFloat gestureDistance = CGPoint([twoFingerDrag translationInView:twoFingerDrag.view]).y; - CGFloat slowdown = 2.0; - - CGFloat pitchNew = initialPitch - (gestureDistance / slowdown); - - CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag]; - - MGLMapCamera *oldCamera = self.camera; - MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew]; - - if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) - { - self.mbglMap.jumpTo(mbgl::CameraOptions() + + CGPoint leftTouchPoint = [twoFingerDrag locationOfTouch:0 inView:twoFingerDrag.view]; + CGPoint rightTouchPoint = [twoFingerDrag locationOfTouch:1 inView:twoFingerDrag.view]; + CLLocationDegrees fingerSlopeAngle = [self angleBetweenPoints:leftTouchPoint endPoint:rightTouchPoint]; + + CGPoint middlePoint = [twoFingerDrag translationInView:twoFingerDrag.view]; + + CLLocationDegrees gestureSlopeAngle = [self angleBetweenPoints:self.dragGestureMiddlePoint endPoint:middlePoint]; + self.dragGestureMiddlePoint = middlePoint; + if (fabs(fingerSlopeAngle) < MGLHorizontalTiltToleranceDegrees && fabs(gestureSlopeAngle) > 60.0 ) { + + CGFloat gestureDistance = middlePoint.y; + CGFloat slowdown = 2.0; + + CGFloat pitchNew = initialPitch - (gestureDistance / slowdown); + + CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag]; + + MGLMapCamera *oldCamera = self.camera; + MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew]; + + if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) + { + self.mbglMap.jumpTo(mbgl::CameraOptions() .withPitch(pitchNew) .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y })); + } + + [self cameraIsChanging]; + } - [self cameraIsChanging]; + } else if (twoFingerDrag.state == UIGestureRecognizerStateEnded || twoFingerDrag.state == UIGestureRecognizerStateCancelled) { [self notifyGestureDidEndWithDrift:NO]; [self unrotateIfNeededForGesture]; + self.dragGestureMiddlePoint = CGPointZero; } } @@ -2296,23 +2325,17 @@ public: - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) + if (gestureRecognizer == _twoFingerDrag) { UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer; if (panGesture.minimumNumberOfTouches == 2) { - CGPoint west = [panGesture locationOfTouch:0 inView:panGesture.view]; - CGPoint east = [panGesture locationOfTouch:1 inView:panGesture.view]; - - if (west.x > east.x) { - CGPoint swap = west; - west = east; - east = swap; - } + CGPoint leftTouchPoint = [panGesture locationOfTouch:0 inView:panGesture.view]; + CGPoint rightTouchPoint = [panGesture locationOfTouch:1 inView:panGesture.view]; - CLLocationDegrees horizontalToleranceDegrees = 60.0; - if ([self angleBetweenPoints:west east:east] > horizontalToleranceDegrees) { + CLLocationDegrees degrees = [self angleBetweenPoints:leftTouchPoint endPoint:rightTouchPoint]; + if (fabs(degrees) > MGLHorizontalTiltToleranceDegrees) { return NO; } } @@ -2334,18 +2357,24 @@ public: - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ]; - return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]); } -- (CLLocationDegrees)angleBetweenPoints:(CGPoint)west east:(CGPoint)east +- (CLLocationDegrees)angleBetweenPoints:(CGPoint)originPoint endPoint:(CGPoint)endPoint { - CGFloat slope = (west.y - east.y) / (west.x - east.x); + if (originPoint.x > endPoint.x) { + CGPoint swap = originPoint; + originPoint = endPoint; + endPoint = swap; + } + + CGFloat x = (endPoint.x - originPoint.x); + CGFloat y = (endPoint.y - originPoint.y); - CGFloat angle = atan(fabs(slope)); - CLLocationDegrees degrees = MGLDegreesFromRadians(angle); + CGFloat angleInRadians = atan2(y, x); + CLLocationDegrees angleInDegrees = MGLDegreesFromRadians(angleInRadians); - return degrees; + return angleInDegrees; } #pragma mark - Attribution - diff --git a/platform/ios/test/MGLMapViewPitchTests.m b/platform/ios/test/MGLMapViewPitchTests.m index b7b18973a1..3e9311dbd4 100644 --- a/platform/ios/test/MGLMapViewPitchTests.m +++ b/platform/ios/test/MGLMapViewPitchTests.m @@ -2,23 +2,35 @@ #import <XCTest/XCTest.h> @interface MockUIPanGestureRecognizer : UIPanGestureRecognizer -@property CGFloat mbx_tiltGestureYTranslation; @property NSUInteger mbx_numberOfFingersForGesture; +@property CGPoint mbx_middlePoint; +@property CGPoint mbx_westPoint; +@property CGPoint mbx_eastPoint; @end @implementation MockUIPanGestureRecognizer - (instancetype)initWithTarget:(id)target action:(SEL)action { if (self = [super initWithTarget:target action:action]) { - self.mbx_tiltGestureYTranslation = 0; self.mbx_numberOfFingersForGesture = 2; + self.mbx_westPoint = CGPointMake(100, 0); + self.mbx_eastPoint = CGPointMake(200, 0); } return self; } - (NSUInteger)numberOfTouches { return self.mbx_numberOfFingersForGesture; } -- (CGPoint)translationInView:(UIView *)view { return CGPointMake(0, self.mbx_tiltGestureYTranslation); } +- (CGPoint)translationInView:(UIView *)view { return self.mbx_middlePoint; } +- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView *)view { + if (touchIndex == 0) { + return self.mbx_westPoint; + } + return self.mbx_eastPoint; +} - (void)setTiltGestureYTranslationForPitchDegrees:(CGFloat)degrees { // The tilt gesture takes the number of screen points the fingers have moved and then divides them by a "slowdown" factor, which happens to be set to 2.0 in -[MGLMapView handleTwoFingerDragGesture:]. - self.mbx_tiltGestureYTranslation = -(degrees * 2.0); + CGFloat mbx_tiltGestureYTranslation = -(degrees * 2.0); + self.mbx_westPoint = CGPointMake(self.mbx_westPoint.x, mbx_tiltGestureYTranslation); + self.mbx_eastPoint = CGPointMake(self.mbx_eastPoint.x, mbx_tiltGestureYTranslation); + self.mbx_middlePoint = CGPointMake(self.mbx_middlePoint.x, mbx_tiltGestureYTranslation); } @end @@ -112,6 +124,25 @@ } } +- (void)testHorizontalTiltGesture { + MockUIPanGestureRecognizer *gesture = [[MockUIPanGestureRecognizer alloc] initWithTarget:self.mapView action:nil]; + gesture.state = UIGestureRecognizerStateBegan; + [self.mapView handleTwoFingerDragGesture:gesture]; + XCTAssertEqual(self.mapView.camera.pitch, 0, @"Pitch should initially be set to 0°."); + + // Tilt gestures should not be triggered on horizontal dragging. + for (NSInteger deltaX = 0; deltaX < 5; deltaX++) { + gesture.mbx_westPoint = CGPointMake(100 - deltaX, 100); + gesture.mbx_eastPoint = CGPointMake(100 - deltaX, 50); + gesture.mbx_middlePoint = CGPointMake((gesture.mbx_westPoint.x + gesture.mbx_westPoint.x)/2, (gesture.mbx_westPoint.y + gesture.mbx_westPoint.y)/2); + + gesture.state = UIGestureRecognizerStateChanged; + + [self.mapView handleTwoFingerDragGesture:gesture]; + XCTAssertEqual(self.mapView.camera.pitch, 0, @"Horizontal dragging should not tilt the map."); + } +} + - (void)testTiltGestureFromInitialTilt { CGFloat initialTilt = 20; CGFloat additionalTilt = 30; |