diff options
author | Jason Wray <jason@kulturny.com> | 2015-08-18 16:25:40 -0400 |
---|---|---|
committer | Jason Wray <jason@kulturny.com> | 2015-08-26 11:23:03 -0700 |
commit | 42dc5336db5179c50b8d1edeca0bef7a27c8cefd (patch) | |
tree | 601c4117661e9aaed3ffd17660946476cf7eb238 | |
parent | d8ed33c7d48dbabe7e6f5e48f148ea2bec823a07 (diff) | |
download | qtlocation-mapboxgl-42dc5336db5179c50b8d1edeca0bef7a27c8cefd.tar.gz |
iOS perspective gesture support
Drag two fingers upward to tilt the map.
Implements #2116
-rw-r--r-- | include/mbgl/ios/MGLMapView.h | 21 | ||||
-rw-r--r-- | include/mbgl/platform/darwin/settings_nsuserdefaults.hpp | 1 | ||||
-rw-r--r-- | ios/app/MBXViewController.mm | 2 | ||||
-rw-r--r-- | platform/darwin/settings_nsuserdefaults.mm | 3 | ||||
-rw-r--r-- | platform/ios/MGLMapView.mm | 111 | ||||
-rw-r--r-- | platform/ios/MGLMapboxEvents.h | 1 | ||||
-rw-r--r-- | platform/ios/MGLMapboxEvents.m | 1 |
7 files changed, 136 insertions, 4 deletions
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index 2b9a3d2913..fc70f3d904 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -74,6 +74,13 @@ IB_DESIGNABLE * The default value of this property is `YES`. */ @property(nonatomic, getter=isRotateEnabled) BOOL rotateEnabled; +/** A Boolean value that determines whether the user may change the pitch (tilt) of the map. + * + * This property controls only user interactions with the map. If you set the value of this property to `NO`, you may still change the pitch of the map programmatically. + * + * The default value of this property is `YES`. */ +@property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled; + /** The compass image view shown in the upper-right when the map is rotated. */ @property (nonatomic, readonly) UIImageView *compassView; @@ -173,6 +180,20 @@ IB_DESIGNABLE /** Resets the map rotation to a northern heading. */ - (IBAction)resetNorth; +/** The pitch of the map (measured in degrees). + * + * The default value `0` shows a completely flat map. Maximum value is `60`. */ +@property (nonatomic) double pitch; + +/** Changes the pitch of the map. + * @param pitch The pitch of the map (measured in degrees) relative to top-down. + * + * Changing the pitch tilts the map without changing the current center coordinate or zoom level. */ +- (void)setPitch:(double)pitch; + +/** Resets the map pitch to head-on. */ +- (IBAction)resetPitch; + #pragma mark - Converting Map Coordinates /** @name Converting Map Coordinates */ diff --git a/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp b/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp index 6c91fd3029..615320ee55 100644 --- a/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp +++ b/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp @@ -17,6 +17,7 @@ public: double latitude = 0; double zoom = 0; double bearing = 0; + double pitch = 0; MGLUserTrackingMode userTrackingMode = MGLUserTrackingModeNone; bool showsUserLocation = false; diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index f7cbf0c042..99c207d24d 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -86,6 +86,7 @@ mbgl::Settings_NSUserDefaults *settings = nullptr; settings->latitude = self.mapView.centerCoordinate.latitude; settings->zoom = self.mapView.zoomLevel; settings->bearing = self.mapView.direction; + settings->pitch = self.mapView.pitch; settings->debug = self.mapView.isDebugActive; settings->userTrackingMode = self.mapView.userTrackingMode; settings->showsUserLocation = self.mapView.showsUserLocation; @@ -99,6 +100,7 @@ mbgl::Settings_NSUserDefaults *settings = nullptr; settings->load(); [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(settings->latitude, settings->longitude) zoomLevel:settings->zoom animated:NO]; self.mapView.direction = settings->bearing; + self.mapView.pitch = settings->pitch; self.mapView.userTrackingMode = settings->userTrackingMode; self.mapView.showsUserLocation = settings->showsUserLocation; [self.mapView setDebugActive:settings->debug]; diff --git a/platform/darwin/settings_nsuserdefaults.mm b/platform/darwin/settings_nsuserdefaults.mm index f6f9bbeedb..5c943baff9 100644 --- a/platform/darwin/settings_nsuserdefaults.mm +++ b/platform/darwin/settings_nsuserdefaults.mm @@ -11,6 +11,7 @@ Settings_NSUserDefaults::Settings_NSUserDefaults() @"latitude" : @(latitude), @"zoom" : @(zoom), @"bearing" : @(bearing), + @"pitch" : @(pitch), @"userTrackingMode" : @(userTrackingMode), @"showsUserLocation" : @(showsUserLocation), @"debug" : @(debug), @@ -26,6 +27,7 @@ void Settings_NSUserDefaults::load() latitude = [settings[@"latitude"] doubleValue]; zoom = [settings[@"zoom"] doubleValue]; bearing = [settings[@"bearing"] doubleValue]; + pitch = [settings[@"pitch"] doubleValue]; debug = [settings[@"debug"] boolValue]; unsigned uncheckedTrackingMode = [settings[@"trackingMode"] unsignedIntValue]; @@ -44,6 +46,7 @@ void Settings_NSUserDefaults::save() @"latitude" : @(latitude), @"zoom" : @(zoom), @"bearing" : @(bearing), + @"pitch" : @(pitch), @"userTrackingMode" : @(userTrackingMode), @"showsUserLocation" : @(showsUserLocation), @"debug" : @(debug), diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index f12836b057..4f3e903074 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -85,6 +85,7 @@ CLLocationDegrees MGLDegreesFromRadians(CGFloat radians) @property (nonatomic) UIPinchGestureRecognizer *pinch; @property (nonatomic) UIRotationGestureRecognizer *rotate; @property (nonatomic) UILongPressGestureRecognizer *quickZoom; +@property (nonatomic) UIPanGestureRecognizer *twoFingerDrag; @property (nonatomic) NSMapTable *annotationIDsByAnnotation; @property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImages; @property (nonatomic) std::vector<uint32_t> annotationsNearbyLastTap; @@ -338,6 +339,15 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) [twoFingerTap requireGestureRecognizerToFail:_pinch]; [twoFingerTap requireGestureRecognizerToFail:_rotate]; [self addGestureRecognizer:twoFingerTap]; + + _twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)]; + _twoFingerDrag.minimumNumberOfTouches = 2; + _twoFingerDrag.maximumNumberOfTouches = 2; + _twoFingerDrag.delegate = self; + [_twoFingerDrag requireGestureRecognizerToFail:twoFingerTap]; + [_twoFingerDrag requireGestureRecognizerToFail:_pan]; + [self addGestureRecognizer:_twoFingerDrag]; + _pitchEnabled = YES; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { @@ -1326,6 +1336,59 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) } } +- (void)handleTwoFingerDragGesture:(UIPanGestureRecognizer *)twoFingerDrag +{ + if ( ! self.isPitchEnabled) return; + + _mbglMap->cancelTransitions(); + + if (twoFingerDrag.state == UIGestureRecognizerStateBegan) + { + [self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag]; + } + else if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged) + { + CGFloat gestureDistance = CGPoint([twoFingerDrag translationInView:twoFingerDrag.view]).y; + double currentPitch = _mbglMap->getPitch(); + double minPitch = 0; + double maxPitch = 60.0; + double slowdown = 20.0; + + double pitchNew = fmax(fmin(currentPitch - (gestureDistance / slowdown), maxPitch), minPitch); + + _mbglMap->setPitch(pitchNew); + } + else if (twoFingerDrag.state == UIGestureRecognizerStateEnded || twoFingerDrag.state == UIGestureRecognizerStateCancelled) + { + [self unrotateIfNeededAnimated:YES]; + + //[self notifyMapChange:(mbgl::MapChangeRegionDidChange)]; + } + +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) + { + UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer; + + if (panGesture.minimumNumberOfTouches == 2) + { + CGPoint velocity = [panGesture velocityInView:panGesture.view]; + double gestureAngle = MGLDegreesFromRadians(atan(velocity.y / velocity.x)); + double horizontalToleranceDegrees = 20.0; + + // cancel if gesture angle is not 90º±20º (more or less vertical) + if ( ! (fabs((fabs(gestureAngle) - 90.0)) < horizontalToleranceDegrees)) + { + return NO; + } + } + } + return YES; +} + - (void)handleCalloutAccessoryTapGesture:(UITapGestureRecognizer *)tap { if ([self.delegate respondsToSelector:@selector(mapView:annotation:calloutAccessoryControlTapped:)]) @@ -1419,6 +1482,11 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) return [NSSet setWithObject:@"allowsRotating"]; } ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitchEnabled +{ + return [NSSet setWithObject:@"allowsPitching"]; +} + - (void)setDebugActive:(BOOL)debugActive { _mbglMap->setDebug(debugActive); @@ -1617,6 +1685,25 @@ mbgl::LatLngBounds MGLLatLngBoundsFromCoordinateBounds(MGLCoordinateBounds coord [self setDirection:direction animated:NO]; } +- (double)pitch +{ + return _mbglMap->getPitch(); +} + +- (void)setPitch:(double)pitch +{ + // constrain pitch to between 0º and 60º + // + _mbglMap->setPitch(fmax(fmin(pitch, 60), 0)); + + //[self notifyMapChange:(mbgl::MapChangeRegionDidChange)]; +} + +- (void)resetPitch +{ + [self setPitch:0]; +} + - (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view { CGPoint convertedPoint = [self convertPoint:point fromView:view]; @@ -2630,10 +2717,11 @@ CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng) { [self updateCompass]; - if (self.pan.state == UIGestureRecognizerStateChanged || - self.pinch.state == UIGestureRecognizerStateChanged || - self.rotate.state == UIGestureRecognizerStateChanged || - self.quickZoom.state == UIGestureRecognizerStateChanged) return; + if (self.pan.state == UIGestureRecognizerStateChanged || + self.pinch.state == UIGestureRecognizerStateChanged || + self.rotate.state == UIGestureRecognizerStateChanged || + self.quickZoom.state == UIGestureRecognizerStateChanged || + self.twoFingerDrag.state == UIGestureRecognizerStateChanged) return; if (self.isAnimatingGesture) return; @@ -3062,6 +3150,21 @@ class MBGLView : public mbgl::View self.rotateEnabled = allowsRotating; } ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsPitching +{ + return [NSSet setWithObject:@"pitchEnabled"]; +} + +- (BOOL)allowsPitching +{ + return self.pitchEnabled; +} + +- (void)setAllowsPitching:(BOOL)allowsPitching +{ + self.pitchEnabled = allowsPitching; +} + - (void)didReceiveMemoryWarning { _mbglMap->onLowMemory(); diff --git a/platform/ios/MGLMapboxEvents.h b/platform/ios/MGLMapboxEvents.h index 9efb53c5ea..45f8094954 100644 --- a/platform/ios/MGLMapboxEvents.h +++ b/platform/ios/MGLMapboxEvents.h @@ -30,6 +30,7 @@ extern NSString *const MGLEventGestureQuickZoom; extern NSString *const MGLEventGesturePanStart; extern NSString *const MGLEventGesturePinchStart; extern NSString *const MGLEventGestureRotateStart; +extern NSString *const MGLEventGesturePitchStart; typedef NS_DICTIONARY_OF(NSString *, id) MGLMapboxEventAttributes; typedef NS_MUTABLE_DICTIONARY_OF(NSString *, id) MGLMutableMapboxEventAttributes; diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m index 314d50dd01..77e68ddde0 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -45,6 +45,7 @@ NSString *const MGLEventGestureQuickZoom = @"QuickZoom"; NSString *const MGLEventGesturePanStart = @"Pan"; NSString *const MGLEventGesturePinchStart = @"Pinch"; NSString *const MGLEventGestureRotateStart = @"Rotation"; +NSString *const MGLEventGesturePitchStart = @"Pitch"; const NSUInteger MGLMaximumEventsPerFlush = 20; const NSTimeInterval MGLFlushInterval = 60; |