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.mm244
1 files changed, 192 insertions, 52 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 0c438d5606..cc86ed1e53 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -1205,6 +1205,8 @@ public:
_mbglMap->cancelTransitions();
+ MGLMapCamera *oldCamera = self.camera;
+
if (pan.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
@@ -1216,8 +1218,15 @@ public:
else if (pan.state == UIGestureRecognizerStateChanged)
{
CGPoint delta = [pan translationInView:pan.view];
- _mbglMap->moveBy({ delta.x, delta.y });
- [pan setTranslation:CGPointZero inView: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])
+ {
+ _mbglMap->moveBy({ delta.x, delta.y });
+ [pan setTranslation:CGPointZero inView:pan.view];
+ }
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1234,7 +1243,13 @@ public:
if (drift)
{
CGPoint offset = CGPointMake(velocity.x * self.decelerationRate / 4, velocity.y * self.decelerationRate / 4);
- _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationInSecondsFromTimeInterval(self.decelerationRate));
+ MGLMapCamera *toCamera = [self cameraByPanningWithTranslation:offset panGesture:pan];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationInSecondsFromTimeInterval(self.decelerationRate));
+ }
}
[self notifyGestureDidEndWithDrift:drift];
@@ -1250,6 +1265,7 @@ public:
MGLEventKeyZoomLevel: @(zoom)
}];
}
+
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch
@@ -1261,6 +1277,7 @@ public:
_mbglMap->cancelTransitions();
CGPoint centerPoint = [self anchorPointForGesture:pinch];
+ MGLMapCamera *oldCamera = self.camera;
if (pinch.state == UIGestureRecognizerStateBegan)
{
@@ -1273,11 +1290,17 @@ public:
else if (pinch.state == UIGestureRecognizerStateChanged)
{
CGFloat newScale = self.scale * pinch.scale;
-
- if (log2(newScale) < _mbglMap->getMinZoom()) return;
-
- _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
-
+ double zoom = log2(newScale);
+ if (zoom < _mbglMap->getMinZoom()) return;
+
+ // Calculates the final camera zoom, has no effect within current map camera.
+ MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ }
// The gesture recognizer only reports the gesture’s current center
// point, so use the previous center point to anchor the transition.
// If the number of touches has changed, the remembered center point is
@@ -1288,7 +1311,6 @@ public:
_mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate),
mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
-
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
else if (pinch.state == UIGestureRecognizerStateEnded || pinch.state == UIGestureRecognizerStateCancelled)
@@ -1321,14 +1343,25 @@ public:
{
velocity = 0;
}
-
- if (velocity && duration)
+
+ BOOL drift = velocity && duration;
+
+ // 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])
{
- _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSecondsFromTimeInterval(duration));
+ drift = NO;
+ } else {
+ if (drift)
+ {
+ _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSecondsFromTimeInterval(duration));
+ }
}
- [self notifyGestureDidEndWithDrift:velocity && duration];
-
+ [self notifyGestureDidEndWithDrift:drift];
[self unrotateIfNeededForGesture];
}
@@ -1343,7 +1376,8 @@ public:
_mbglMap->cancelTransitions();
CGPoint centerPoint = [self anchorPointForGesture:rotate];
-
+ MGLMapCamera *oldCamera = self.camera;
+
if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
@@ -1368,10 +1402,17 @@ public:
newDegrees = fminf(newDegrees, 30);
newDegrees = fmaxf(newDegrees, -30);
}
-
- _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
-
+
+ MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ }
+
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
+
}
else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
{
@@ -1383,16 +1424,22 @@ public:
CGFloat newRadians = radians + velocity * decelerationRate * 0.1;
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
- _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSecondsFromTimeInterval(decelerationRate));
-
- [self notifyGestureDidEndWithDrift:YES];
-
- __weak MGLMapView *weakSelf = self;
-
- [self animateWithDelay:decelerationRate animations:^
- {
- [weakSelf unrotateIfNeededForGesture];
- }];
+ MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSecondsFromTimeInterval(decelerationRate));
+
+ [self notifyGestureDidEndWithDrift:YES];
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:decelerationRate animations:^
+ {
+ [weakSelf unrotateIfNeededForGesture];
+ }];
+ }
}
else
{
@@ -1508,18 +1555,27 @@ public:
if (doubleTap.state == UIGestureRecognizerStateEnded)
{
- [self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
+ MGLMapCamera *oldCamera = self.camera;
+
CGPoint gesturePoint = [self anchorPointForGesture:doubleTap];
-
- mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
- _mbglMap->scaleBy(2, center, MGLDurationInSecondsFromTimeInterval(MGLAnimationDuration));
-
- __weak MGLMapView *weakSelf = self;
-
- [self animateWithDelay:MGLAnimationDuration animations:^
+
+ MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:self.zoomLevel + 1.0 aroundAnchorPoint:gesturePoint];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- [weakSelf unrotateIfNeededForGesture];
- }];
+ [self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
+
+ mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
+ _mbglMap->scaleBy(2, center, MGLDurationInSecondsFromTimeInterval(MGLAnimationDuration));
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:MGLAnimationDuration animations:^
+ {
+ [weakSelf unrotateIfNeededForGesture];
+ }];
+ }
}
}
@@ -1537,17 +1593,26 @@ public:
}
else if (twoFingerTap.state == UIGestureRecognizerStateEnded)
{
- CGPoint gesturePoint = [self anchorPointForGesture:twoFingerTap];
+ MGLMapCamera *oldCamera = self.camera;
- mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
- _mbglMap->scaleBy(0.5, center, MGLDurationInSecondsFromTimeInterval(MGLAnimationDuration));
-
- __weak MGLMapView *weakSelf = self;
-
- [self animateWithDelay:MGLAnimationDuration animations:^
+ double zoom = self.zoomLevel;
+ CGPoint gesturePoint = [self anchorPointForGesture:twoFingerTap];
+
+ MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom - 1.0 aroundAnchorPoint:gesturePoint];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- [weakSelf unrotateIfNeededForGesture];
- }];
+ mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
+ _mbglMap->scaleBy(0.5, center, MGLDurationInSecondsFromTimeInterval(MGLAnimationDuration));
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:MGLAnimationDuration animations:^
+ {
+ [weakSelf unrotateIfNeededForGesture];
+ }];
+ }
}
}
@@ -1556,7 +1621,7 @@ public:
if ( ! self.isZoomEnabled) return;
_mbglMap->cancelTransitions();
-
+
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
@@ -1576,9 +1641,21 @@ public:
if (newZoom < _mbglMap->getMinZoom()) return;
CGPoint centerPoint = [self anchorPointForGesture:quickZoom];
-
- _mbglMap->scaleBy(powf(2, newZoom) / _mbglMap->getScale(),
- mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+
+ MGLMapCamera *oldCamera = self.camera;
+
+ double zoom = self.zoomLevel;
+ double scale = powf(2, newZoom) / _mbglMap->getScale();
+
+ double estimatedZoom = zoom * scale;
+
+ MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:estimatedZoom aroundAnchorPoint:centerPoint];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ _mbglMap->scaleBy(scale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ }
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1594,6 +1671,7 @@ public:
if ( ! self.isPitchEnabled) return;
_mbglMap->cancelTransitions();
+ MGLMapCamera *oldCamera = self.camera;
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
@@ -1610,7 +1688,13 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag];
- _mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew];
+
+ if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
+ [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ _mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ }
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1619,6 +1703,62 @@ public:
[self notifyGestureDidEndWithDrift:NO];
[self unrotateIfNeededForGesture];
}
+
+}
+
+- (MGLMapCamera *)cameraByPanningWithTranslation:(CGPoint)endPoint panGesture:(UIPanGestureRecognizer *)pan
+{
+ MGLMapCamera *panCamera = [self.camera copy];
+
+ CGPoint centerPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
+ CGPoint endCameraPoint = CGPointMake(centerPoint.x - endPoint.x, centerPoint.y - endPoint.y);
+ CLLocationCoordinate2D panCoordinate = [self convertPoint:endCameraPoint toCoordinateFromView:pan.view];
+
+ panCamera.centerCoordinate = panCoordinate;
+
+ return panCamera;
+}
+
+- (MGLMapCamera *)cameraByZoomingToZoomLevel:(double)zoom aroundAnchorPoint:(CGPoint)anchorPoint
+{
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ mbgl::CameraOptions currentCameraOptions = _mbglMap->getCameraOptions(padding);
+ MGLMapCamera *camera;
+
+ mbgl::ScreenCoordinate anchor = mbgl::ScreenCoordinate { anchorPoint.x, anchorPoint.y };
+ currentCameraOptions.zoom = mbgl::util::clamp(zoom, self.minimumZoomLevel, self.maximumZoomLevel);
+ currentCameraOptions.anchor = anchor;
+ camera = [self cameraForCameraOptions:currentCameraOptions];
+
+ return camera;
+}
+
+- (MGLMapCamera *)cameraByRotatingToDirection:(CLLocationDirection)degrees aroundAnchorPoint:(CGPoint)anchorPoint
+{
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ mbgl::CameraOptions currentCameraOptions = _mbglMap->getCameraOptions(padding);
+
+ MGLMapCamera *camera;
+
+ mbgl::ScreenCoordinate anchor = mbgl::ScreenCoordinate { anchorPoint.x, anchorPoint.y };
+ currentCameraOptions.angle = degrees * mbgl::util::DEG2RAD;
+ currentCameraOptions.anchor = anchor;
+ camera = [self cameraForCameraOptions:currentCameraOptions];
+
+ return camera;
+}
+
+- (MGLMapCamera *)cameraByTiltingToPitch:(CGFloat)pitch
+{
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ mbgl::CameraOptions currentCameraOptions = _mbglMap->getCameraOptions(padding);
+
+ MGLMapCamera *camera;
+
+ currentCameraOptions.pitch = pitch * mbgl::util::DEG2RAD;
+ camera = [self cameraForCameraOptions:currentCameraOptions];
+
+ return camera;
}
- (CGPoint)anchorPointForGesture:(UIGestureRecognizer *)gesture {