summaryrefslogtreecommitdiff
path: root/platform/ios/src/MGLMapView.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src/MGLMapView.mm')
-rw-r--r--platform/ios/src/MGLMapView.mm190
1 files changed, 144 insertions, 46 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 31eac056e5..cad14343a8 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -210,6 +210,8 @@ public:
@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
@property (nonatomic) UIPanGestureRecognizer *twoFingerDrag;
+@property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask;
+
/// Mapping from reusable identifiers to annotation images.
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
@@ -506,10 +508,6 @@ public:
_doubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:_doubleTap];
- _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
- [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTap];
- _singleTapGestureRecognizer.delegate = self;
- [self addGestureRecognizer:_singleTapGestureRecognizer];
_twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)];
_twoFingerDrag.minimumNumberOfTouches = 2;
@@ -534,16 +532,26 @@ public:
[_quickZoom requireGestureRecognizerToFail:_doubleTap];
[self addGestureRecognizer:_quickZoom];
+ _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapGesture:)];
+ [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTap];
+ _singleTapGestureRecognizer.delegate = self;
+ [_singleTapGestureRecognizer requireGestureRecognizerToFail:_quickZoom];
+ [self addGestureRecognizer:_singleTapGestureRecognizer];
+
// observe app activity
//
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationDidBecomeActiveNotification object:nil];
+ // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `sleepGL:` in response to it, as doing
+ // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+
// set initial position
//
mbgl::CameraOptions options;
@@ -551,6 +559,9 @@ public:
mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
options.padding = padding;
options.zoom = 0;
+
+ _cameraChangeReasonBitmask = MGLCameraChangeReasonNone;
+
_mbglMap->jumpTo(options);
_pendingLatitude = NAN;
_pendingLongitude = NAN;
@@ -706,7 +717,7 @@ public:
{
MGLAssertIsMainThread();
- _rendererFrontend->onLowMemory();
+ _rendererFrontend->reduceMemoryUse();
}
#pragma mark - Layout -
@@ -1148,6 +1159,13 @@ public:
- (void)sleepGL:(__unused NSNotification *)notification
{
MGLAssertIsMainThread();
+
+ // Ideally we would wait until we actually received a memory warning but the bulk of the memory
+ // we have to release is tied up in GL buffers that we can't touch once we're in the background.
+ // Compromise position: release everything but currently rendering tiles
+ // A possible improvement would be to store a copy of the GL buffers that we could use to rapidly
+ // restart, but that we could also discard in response to a memory warning.
+ _rendererFrontend->reduceMemoryUse();
if ( ! self.dormant)
{
@@ -1237,6 +1255,8 @@ public:
- (void)handleCompassTapGesture:(__unused id)sender
{
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonResetNorth;
+
[self resetNorthAnimated:YES];
if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading ||
@@ -1259,6 +1279,7 @@ public:
- (void)notifyGestureDidBegin {
BOOL animated = NO;
+
[self cameraWillChangeAnimated:animated];
_mbglMap->setGestureInProgress(true);
_changeDelimiterSuppressionDepth++;
@@ -1282,6 +1303,23 @@ public:
return _changeDelimiterSuppressionDepth > 0;
}
+- (BOOL)_shouldChangeFromCamera:(nonnull MGLMapCamera *)oldCamera toCamera:(nonnull MGLMapCamera *)newCamera
+{
+ // Check delegates first
+ if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:reason:)])
+ {
+ return [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:newCamera reason:self.cameraChangeReasonBitmask];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)])
+ {
+ return [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:newCamera];
+ }
+ else
+ {
+ return YES;
+ }
+}
+
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
if ( ! self.isScrollEnabled) return;
@@ -1289,7 +1327,9 @@ public:
_mbglMap->cancelTransitions();
MGLMapCamera *oldCamera = self.camera;
-
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGesturePan;
+
if (pan.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
@@ -1303,9 +1343,8 @@ public:
CGPoint delta = [pan translationInView:pan.view];
MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:delta panGesture:pan];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->moveBy({ delta.x, delta.y });
[pan setTranslation:CGPointZero inView:pan.view];
@@ -1327,9 +1366,8 @@ public:
{
CGPoint offset = CGPointMake(velocity.x * self.decelerationRate / 4, velocity.y * self.decelerationRate / 4);
MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:offset panGesture:pan];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->moveBy({ offset.x, offset.y }, MGLDurationFromTimeInterval(self.decelerationRate));
}
@@ -1360,6 +1398,8 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:pinch];
MGLMapCamera *oldCamera = self.camera;
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGesturePinch;
+
if (pinch.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
@@ -1376,9 +1416,8 @@ public:
// Calculates the final camera zoom, has no effect within current map camera.
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
// The gesture recognizer only reports the gesture’s current center
@@ -1430,9 +1469,8 @@ public:
// Calculates the final camera zoom, this has no effect within current map camera.
double zoom = log2(newScale);
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint];
-
- if ([self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)]
- && ![self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
drift = NO;
} else {
@@ -1458,7 +1496,9 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:rotate];
MGLMapCamera *oldCamera = self.camera;
-
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureRotate;
+
if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
@@ -1485,9 +1525,8 @@ public:
}
MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
@@ -1505,9 +1544,8 @@ public:
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationFromTimeInterval(decelerationRate));
@@ -1552,6 +1590,7 @@ public:
}
[self deselectAnnotation:self.selectedAnnotation animated:YES];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement);
+
return;
}
@@ -1562,7 +1601,7 @@ public:
CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint];
[self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect];
}
- else
+ else if (self.selectedAnnotation)
{
[self deselectAnnotation:self.selectedAnnotation animated:YES];
}
@@ -1644,6 +1683,8 @@ public:
if (doubleTap.state == UIGestureRecognizerStateEnded)
{
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomIn;
+
MGLMapCamera *oldCamera = self.camera;
double newZoom = round(self.zoomLevel) + 1.0;
@@ -1651,9 +1692,8 @@ public:
CGPoint gesturePoint = [self anchorPointForGesture:doubleTap];
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:gesturePoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
[self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
@@ -1680,9 +1720,13 @@ public:
_mbglMap->cancelTransitions();
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomOut;
+
if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
+
+ [self notifyGestureDidBegin];
}
else if (twoFingerTap.state == UIGestureRecognizerStateEnded)
{
@@ -1693,9 +1737,8 @@ public:
CGPoint gesturePoint = [self anchorPointForGesture:twoFingerTap];
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:gesturePoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
_mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration));
@@ -1715,7 +1758,9 @@ public:
if ( ! self.isZoomEnabled) return;
_mbglMap->cancelTransitions();
-
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureOneFingerZoom;
+
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
@@ -1738,9 +1783,8 @@ public:
MGLMapCamera *oldCamera = self.camera;
MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint];
-
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
@@ -1760,6 +1804,8 @@ public:
_mbglMap->cancelTransitions();
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureTilt;
+
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
@@ -1779,8 +1825,7 @@ public:
MGLMapCamera *oldCamera = self.camera;
MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew];
- if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
- [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
_mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
@@ -2278,7 +2323,7 @@ public:
- (void)emptyMemoryCache
{
- _rendererFrontend->onLowMemory();
+ _rendererFrontend->reduceMemoryUse();
}
- (void)setZoomEnabled:(BOOL)zoomEnabled
@@ -2871,6 +2916,8 @@ public:
{
self.userTrackingMode = MGLUserTrackingModeNone;
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
[self _setCenterCoordinate:centerCoordinate edgePadding:self.contentInset zoomLevel:zoomLevel direction:direction duration:animated ? MGLAnimationDuration : 0 animationTimingFunction:nil completionHandler:completion];
}
@@ -2920,6 +2967,9 @@ public:
}
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
_mbglMap->easeTo(cameraOptions, animationOptions);
}
@@ -2943,6 +2993,8 @@ public:
if (zoomLevel == self.zoomLevel) return;
_mbglMap->cancelTransitions();
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
CGFloat duration = animated ? MGLAnimationDuration : 0;
_mbglMap->setZoom(zoomLevel,
@@ -3031,6 +3083,9 @@ public:
- (void)setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction duration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion
{
self.userTrackingMode = MGLUserTrackingModeNone;
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
[self _setVisibleCoordinates:coordinates count:count edgePadding:insets direction:direction duration:duration animationTimingFunction:function completionHandler:completion];
}
@@ -3080,6 +3135,9 @@ public:
[self willChangeValueForKey:@"visibleCoordinateBounds"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"visibleCoordinateBounds"];
}
@@ -3113,6 +3171,8 @@ public:
CGFloat duration = animated ? MGLAnimationDuration : 0;
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
if (self.userTrackingMode == MGLUserTrackingModeNone)
{
_mbglMap->setBearing(direction,
@@ -3197,6 +3257,9 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
@@ -3253,6 +3316,9 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
+
+ self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic;
+
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets];
_mbglMap->flyTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
@@ -3413,6 +3479,13 @@ public:
return [self metersPerPointAtLatitude:latitude];
}
+#pragma mark - Camera Change Reason -
+
+- (void)resetCameraChangeReason
+{
+ self.cameraChangeReasonBitmask = MGLCameraChangeReasonNone;
+}
+
#pragma mark - Styling -
- (NS_ARRAY_OF(NSURL *) *)bundledStyleURLs
@@ -5359,9 +5432,16 @@ public:
}
}
- if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)])
+ if ( ! [self isSuppressingChangeDelimiters] )
{
- [self.delegate mapView:self regionWillChangeAnimated:animated];
+ if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeWithReason:animated:)])
+ {
+ [self.delegate mapView:self regionWillChangeWithReason:self.cameraChangeReasonBitmask animated:animated];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)])
+ {
+ [self.delegate mapView:self regionWillChangeAnimated:animated];
+ }
}
}
@@ -5375,8 +5455,12 @@ public:
if (!self.scaleBar.hidden) {
[(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]];
}
-
- if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)])
+
+ if ([self.delegate respondsToSelector:@selector(mapView:regionIsChangingWithReason:)])
+ {
+ [self.delegate mapView:self regionIsChangingWithReason:self.cameraChangeReasonBitmask];
+ }
+ else if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)])
{
[self.delegate mapViewRegionIsChanging:self];
}
@@ -5389,9 +5473,13 @@ public:
[self updateCompass];
- if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)])
+ if ( ! [self isSuppressingChangeDelimiters])
{
- if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
+ BOOL respondsToSelector = [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)];
+ BOOL respondsToSelectorWithReason = [self.delegate respondsToSelector:@selector(mapView:regionDidChangeWithReason:animated:)];
+
+ if ((respondsToSelector || respondsToSelectorWithReason) &&
+ ([UIApplication sharedApplication].applicationState == UIApplicationStateActive))
{
_featureAccessibilityElements = nil;
_visiblePlaceFeatures = nil;
@@ -5403,7 +5491,17 @@ public:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
}
- [self.delegate mapView:self regionDidChangeAnimated:animated];
+
+ if (respondsToSelectorWithReason)
+ {
+ [self.delegate mapView:self regionDidChangeWithReason:self.cameraChangeReasonBitmask animated:animated];
+ }
+ else if (respondsToSelector)
+ {
+ [self.delegate mapView:self regionDidChangeAnimated:animated];
+ }
+
+ [self resetCameraChangeReason];
}
}