[ios] Add threshold for triggering the rotate gesture recognizer (#14929)
This commit is contained in:
@@ -388,7 +388,7 @@
|
||||
9680274022653B84006BA4A1 /* MBXSKUToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 9680273E22653B84006BA4A1 /* MBXSKUToken.h */; };
|
||||
9680276422655696006BA4A1 /* libmbxaccounts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9680274122653C3E006BA4A1 /* libmbxaccounts.a */; };
|
||||
96802766226556C5006BA4A1 /* libmbxaccounts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9680274122653C3E006BA4A1 /* libmbxaccounts.a */; };
|
||||
9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.m */; };
|
||||
9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.mm */; };
|
||||
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; };
|
||||
96E516DC2000547000A02306 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; };
|
||||
@@ -481,6 +481,8 @@
|
||||
9C6E284522A982670056B7BE /* MMEUINavigation.h in Headers */ = {isa = PBXBuildFile; fileRef = 406E99B31FFEFED600D9FFCC /* MMEUINavigation.h */; };
|
||||
9C6E284622A982670056B7BE /* MMEUniqueIdentifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 40834BBF1FE05D6E00C1BD0D /* MMEUniqueIdentifier.h */; };
|
||||
9C6E284722A982670056B7BE /* MMEDispatchManager.h in Headers */ = {isa = PBXBuildFile; fileRef = ACA65F552140696B00537748 /* MMEDispatchManager.h */; };
|
||||
A4DE3DCB23038C98005B3473 /* MGLMockGestureRecognizers.h in Sources */ = {isa = PBXBuildFile; fileRef = A4DE3DCA23038A7F005B3473 /* MGLMockGestureRecognizers.h */; };
|
||||
A4DE3DCC23038CCA005B3473 /* MGLMockGestureRecognizers.m in Sources */ = {isa = PBXBuildFile; fileRef = A4DE3DC823038A07005B3473 /* MGLMockGestureRecognizers.m */; };
|
||||
A4F3FB1D2254865900A30170 /* missing_icon.json in Resources */ = {isa = PBXBuildFile; fileRef = A4F3FB1C2254865900A30170 /* missing_icon.json */; };
|
||||
AC46EB59225E600A0039C013 /* MMECertPin.h in Headers */ = {isa = PBXBuildFile; fileRef = AC46EB57225E60090039C013 /* MMECertPin.h */; };
|
||||
AC46EB5A225E600A0039C013 /* MMECertPin.h in Headers */ = {isa = PBXBuildFile; fileRef = AC46EB57225E60090039C013 /* MMECertPin.h */; };
|
||||
@@ -1129,7 +1131,7 @@
|
||||
967C864A210A9D3C004DF794 /* UIDevice+MGLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+MGLAdditions.m"; sourceTree = "<group>"; };
|
||||
9680273E22653B84006BA4A1 /* MBXSKUToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MBXSKUToken.h; path = "../vendor/mapbox-accounts-ios/MBXSKUToken.h"; sourceTree = "<group>"; };
|
||||
9680274122653C3E006BA4A1 /* libmbxaccounts.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbxaccounts.a; path = "vendor/mapbox-accounts-ios/libmbxaccounts.a"; sourceTree = SOURCE_ROOT; };
|
||||
9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewZoomTests.m; sourceTree = "<group>"; };
|
||||
9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapViewZoomTests.mm; sourceTree = "<group>"; };
|
||||
968F36B41E4D0FC6003A5522 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
96E027241E57C76E004B8E66 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
96E027271E57C77A004B8E66 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@@ -1173,6 +1175,8 @@
|
||||
9C6E286522A9849E0056B7BE /* release-notes-jazzy.md.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = "release-notes-jazzy.md.ejs"; sourceTree = "<group>"; };
|
||||
9C6E286622A9849E0056B7BE /* deploy-snapshot.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "deploy-snapshot.sh"; sourceTree = "<group>"; };
|
||||
9C6E286722A9849E0056B7BE /* release-notes.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "release-notes.js"; sourceTree = "<group>"; };
|
||||
A4DE3DC823038A07005B3473 /* MGLMockGestureRecognizers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMockGestureRecognizers.m; sourceTree = "<group>"; };
|
||||
A4DE3DCA23038A7F005B3473 /* MGLMockGestureRecognizers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMockGestureRecognizers.h; sourceTree = "<group>"; };
|
||||
A4F3FB1C2254865900A30170 /* missing_icon.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = missing_icon.json; sourceTree = "<group>"; };
|
||||
AC46EB57225E60090039C013 /* MMECertPin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMECertPin.h; sourceTree = "<group>"; };
|
||||
AC46EB58225E60090039C013 /* MMECertPin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMECertPin.m; sourceTree = "<group>"; };
|
||||
@@ -1744,6 +1748,8 @@
|
||||
children = (
|
||||
CA5E5042209BDC5F001A8A81 /* MGLTestUtility.h */,
|
||||
4031ACFE1E9FD29F00A3EA26 /* MGLSDKTestHelpers.swift */,
|
||||
A4DE3DCA23038A7F005B3473 /* MGLMockGestureRecognizers.h */,
|
||||
A4DE3DC823038A07005B3473 /* MGLMockGestureRecognizers.m */,
|
||||
);
|
||||
name = "Test Helpers";
|
||||
sourceTree = "<group>";
|
||||
@@ -2057,7 +2063,7 @@
|
||||
96381C0122C6F3950053497D /* MGLMapViewPitchTests.m */,
|
||||
9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */,
|
||||
076171C22139C70900668A35 /* MGLMapViewTests.m */,
|
||||
9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.m */,
|
||||
9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.mm */,
|
||||
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
|
||||
96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */,
|
||||
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
|
||||
@@ -3236,6 +3242,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A4DE3DCC23038CCA005B3473 /* MGLMockGestureRecognizers.m in Sources */,
|
||||
A4DE3DCB23038C98005B3473 /* MGLMockGestureRecognizers.h in Sources */,
|
||||
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */,
|
||||
DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */,
|
||||
409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
|
||||
@@ -3273,7 +3281,7 @@
|
||||
3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */,
|
||||
9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */,
|
||||
409D0A0D1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift in Sources */,
|
||||
9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.m in Sources */,
|
||||
9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.mm in Sources */,
|
||||
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
|
||||
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */,
|
||||
07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */,
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import <Mapbox/Mapbox.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "MGLMockGestureRecognizers.h"
|
||||
|
||||
#import <mbgl/math/wrap.hpp>
|
||||
|
||||
@@ -8,12 +9,6 @@
|
||||
- (void)resetNorthAnimated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
@interface UIRotationGestureRecognizerMock : UIRotationGestureRecognizer
|
||||
@end
|
||||
|
||||
@implementation UIRotationGestureRecognizerMock
|
||||
- (CGPoint)locationInView:(nullable UIView*)view { return view.center; }
|
||||
@end
|
||||
|
||||
@interface MGLMapViewDirectionTests : XCTestCase
|
||||
@property (nonatomic) MGLMapView *mapView;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#import <Mapbox/Mapbox.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "MGLMockGestureRecognizers.h"
|
||||
|
||||
#import <mbgl/math/wrap.hpp>
|
||||
|
||||
@interface MGLMapView (MGLMapViewZoomTests)
|
||||
@property (nonatomic) BOOL isZooming;
|
||||
@property (nonatomic) CGFloat rotationThresholdWhileZooming;
|
||||
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch;
|
||||
@end
|
||||
|
||||
@interface UIPinchGestureRecognizerMock : UIPinchGestureRecognizer
|
||||
@property (nonatomic) CGPoint locationInViewOverride;
|
||||
@end
|
||||
|
||||
@implementation UIPinchGestureRecognizerMock
|
||||
- (CGPoint)locationInView:(nullable UIView *)view { return self.locationInViewOverride; }
|
||||
- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate;
|
||||
@end
|
||||
|
||||
@interface MGLMapViewZoomTests : XCTestCase
|
||||
@@ -129,6 +127,60 @@
|
||||
XCTAssertEqualWithAccuracy(centerCoordinateBeforeReset.longitude, self.mapView.centerCoordinate.longitude, 0.0000001, @"Map center coordinate longitude should remain constant after resetting to north.");
|
||||
}
|
||||
|
||||
- (void)testPinchAndZoom {
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@3 forKey:@"MGLRotationThresholdWhileZooming"];
|
||||
self.mapView.rotationThresholdWhileZooming = 3;
|
||||
self.mapView.zoomLevel = 15;
|
||||
UIPinchGestureRecognizerMock *pinch = [[UIPinchGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
|
||||
[self.mapView addGestureRecognizer:pinch];
|
||||
pinch.state = UIGestureRecognizerStateBegan;
|
||||
pinch.velocity = 5.0;
|
||||
pinch.locationInViewOverride = CGPointMake(0, 0);
|
||||
[self.mapView handlePinchGesture:pinch];
|
||||
|
||||
XCTAssertTrue(self.mapView.isZooming);
|
||||
|
||||
UIRotationGestureRecognizerMock *rotate = [[UIRotationGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
|
||||
rotate.state = UIGestureRecognizerStateBegan;
|
||||
rotate.rotation = MGLRadiansFromDegrees(1);
|
||||
[self.mapView addGestureRecognizer:rotate];
|
||||
[self.mapView handleRotateGesture:rotate];
|
||||
|
||||
// Both the rotation and direction should be zero since the rotation threshold hasn't been met.
|
||||
XCTAssertEqual(rotate.rotation, 0);
|
||||
XCTAssertEqual(self.mapView.direction, 0);
|
||||
|
||||
// The direction should be `0`. The default rotation threshold is `3`.
|
||||
XCTAssertEqual(self.mapView.direction, 0);
|
||||
rotate.state = UIGestureRecognizerStateChanged;
|
||||
rotate.rotation = MGLRadiansFromDegrees(2);
|
||||
[self.mapView handleRotateGesture:rotate];
|
||||
|
||||
// The direction should be `0`. The default rotation threshold is `3`.
|
||||
XCTAssertEqual(self.mapView.direction, 0);
|
||||
|
||||
for (NSNumber *degrees in @[@-90, @-10, @10, @10, @30, @90, @180, @240, @460, @500, @590, @800]) {
|
||||
rotate.state = UIGestureRecognizerStateChanged;
|
||||
rotate.rotation = MGLRadiansFromDegrees([degrees doubleValue]);
|
||||
[self.mapView handleRotateGesture:rotate];
|
||||
|
||||
CGFloat wrappedRotation = mbgl::util::wrap(-MGLDegreesFromRadians(rotate.rotation), 0., 360.);
|
||||
|
||||
|
||||
// Check that the direction property now matches the gesture's rotation.
|
||||
XCTAssertEqualWithAccuracy(self.mapView.direction, wrappedRotation, 0.001, @"Map direction should match gesture rotation for input of %@°.", degrees);
|
||||
}
|
||||
|
||||
rotate.state = UIGestureRecognizerStateEnded;
|
||||
pinch.state = UIGestureRecognizerStateEnded;
|
||||
|
||||
[self.mapView handleRotateGesture:rotate];
|
||||
[self.mapView handlePinchGesture:pinch];
|
||||
|
||||
XCTAssertFalse(self.mapView.isZooming);
|
||||
}
|
||||
|
||||
NS_INLINE CGFloat MGLScaleFromZoomLevel(double zoom) {
|
||||
return pow(2, zoom);
|
||||
}
|
||||
10
platform/ios/test/MGLMockGestureRecognizers.h
Normal file
10
platform/ios/test/MGLMockGestureRecognizers.h
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIPinchGestureRecognizerMock : UIPinchGestureRecognizer
|
||||
@property (nonatomic, readwrite) CGFloat velocity;
|
||||
@property (nonatomic) CGPoint locationInViewOverride;
|
||||
@end
|
||||
|
||||
@interface UIRotationGestureRecognizerMock : UIRotationGestureRecognizer
|
||||
@end
|
||||
11
platform/ios/test/MGLMockGestureRecognizers.m
Normal file
11
platform/ios/test/MGLMockGestureRecognizers.m
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
#import "MGLMockGestureRecognizers.h"
|
||||
|
||||
@implementation UIPinchGestureRecognizerMock
|
||||
@synthesize velocity;
|
||||
- (CGPoint)locationInView:(nullable UIView *)view { return self.locationInViewOverride; }
|
||||
@end
|
||||
|
||||
@implementation UIRotationGestureRecognizerMock
|
||||
- (CGPoint)locationInView:(nullable UIView*)view { return view.center; }
|
||||
@end
|
||||
Reference in New Issue
Block a user