summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Guerra Soto <fabian.guerra@mapbox.com>2019-08-23 17:59:38 -0700
committerGitHub <noreply@github.com>2019-08-23 17:59:38 -0700
commit5c066f874c1dbf699d58aea967b13a7daea1513e (patch)
treefa77e21014405f32e3921a33380e63ab0b9a87bb
parent5c22f3f236c68385e09b4ea0495badd947404480 (diff)
downloadqtlocation-mapboxgl-5c066f874c1dbf699d58aea967b13a7daea1513e.tar.gz
[ios] Improve tilt gesture recognizer detection. (#15349)
* [ios] Improve tilt gesture recognizer detection. * [ios] Update tilt gesture detection angles. * [ios] Fix a delay at the beginning of the drag gesture. * [ios] Update tilt gesture recognizer tests. * [ios] Update changelog. * [ios] Add horizontal tilt gesture test. * [ios] Addresses review feedback. * [ios] Add comments to clarify the tilt gesture initialization.
-rw-r--r--platform/ios/CHANGELOG.md4
-rw-r--r--platform/ios/src/MGLMapView.mm93
-rw-r--r--platform/ios/test/MGLMapViewPitchTests.m39
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;