[ios] Maintain user tracking, anchor on user dot during zoom, rotate

Per MapKit behavior, zooming or rotating with a gesture can no longer kick the user out of user tracking mode. In user tracking mode, the zoom animation is always anchored on the user dot, wherever it may be.
This commit is contained in:
Minh Nguyễn
2016-01-19 11:06:24 -08:00
committed by Minh Nguyễn
parent 65f6eb25b5
commit 5a00427386
2 changed files with 40 additions and 44 deletions

View File

@@ -48,7 +48,7 @@ Known issues:
- MGLMapCamera’s `altitude` values now match those of MKMapCamera. ([#3362](https://github.com/mapbox/mapbox-gl-native/pull/3362))
- MGLMapView properties like `centerCoordinate` and `camera` now offset the center to account for any translucent top or bottom bar. As a result, when user tracking is enabled and the map view is an immediate child of a view controller, the user dot is centered in the unobscured portion of the map view. To override this offset, modify the `contentInset` property; you may also need to set the containing view controller’s `automaticallyAdjustsScrollViewInsets` property to `NO`. ([#3583](https://github.com/mapbox/mapbox-gl-native/pull/3583))
- In user tracking mode, the user dot stays in a fixed position within MGLMapView while the map pans smoothly. A new property, `userLocationVerticalAlignment`, determines the user dot’s fixed position. ([#3589](https://github.com/mapbox/mapbox-gl-native/pull/3589))
- The user dot’s callout view is now centered above the user dot. It was previously offset slightly to the left. ([#3261](https://github.com/mapbox/mapbox-gl-native/pull/3261))
- Zooming and rotation gestures no longer disable user tracking mode. ([#3589](https://github.com/mapbox/mapbox-gl-native/pull/3589))
- Fixed an issue with small map views not properly fitting annotations within bounds. (#[3407](https://github.com/mapbox/mapbox-gl-native/pull/3407))
- When the user rotates the map to within 7° of true north, the map view now snaps to true north. ([#3403](https://github.com/mapbox/mapbox-gl-native/pull/3403))
- The map view’s background can now be transparent or translucent, as long as the style’s background layer is transparent or translucent and `MGLMapView.opaque` is set to `NO`. ([#3096](https://github.com/mapbox/mapbox-gl-native/pull/3096))

View File

@@ -849,6 +849,13 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
return UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
}
/// Returns the center point of the inset content within the map view.
- (CGPoint)contentCenter
{
CGRect contentFrame = self.contentFrame;
return CGPointMake(CGRectGetMidX(contentFrame), CGRectGetMidY(contentFrame));
}
#pragma mark - Life Cycle -
- (void)updateFromDisplayLink
@@ -1094,14 +1101,18 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
if (_mbglMap->getZoom() <= _mbglMap->getMinZoom() && pinch.scale < 1) return;
_mbglMap->cancelTransitions();
CGPoint centerPoint = [pinch locationInView:pinch.view];
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
centerPoint = self.userLocationAnnotationViewCenter;
}
if (pinch.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
self.scale = _mbglMap->getScale();
self.userTrackingMode = MGLUserTrackingModeNone;
[self notifyGestureDidBegin];
}
@@ -1110,10 +1121,8 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
CGFloat newScale = self.scale * pinch.scale;
if (log2(newScale) < _mbglMap->getMinZoom()) return;
mbgl::PrecisionPoint center([pinch locationInView:pinch.view].x,
[pinch locationInView:pinch.view].y);
_mbglMap->setScale(newScale, center);
_mbglMap->setScale(newScale, { centerPoint.x, centerPoint.y });
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1149,9 +1158,7 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
if (velocity)
{
CGPoint pinchCenter = [pinch locationInView:pinch.view];
mbgl::PrecisionPoint center(pinchCenter.x, pinchCenter.y);
_mbglMap->setScale(newScale, center, MGLDurationInSeconds(duration));
_mbglMap->setScale(newScale, { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
}
[self notifyGestureDidEndWithDrift:velocity];
@@ -1165,14 +1172,18 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
if ( ! self.isRotateEnabled) return;
_mbglMap->cancelTransitions();
CGPoint centerPoint = [rotate locationInView:rotate.view];
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
centerPoint = self.userLocationAnnotationViewCenter;
}
if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
self.angle = MGLRadiansFromDegrees(_mbglMap->getBearing()) * -1;
self.userTrackingMode = MGLUserTrackingModeNone;
[self notifyGestureDidBegin];
}
@@ -1187,10 +1198,8 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
newDegrees = fminf(newDegrees, 30);
newDegrees = fmaxf(newDegrees, -30);
}
mbgl::PrecisionPoint center([rotate locationInView:rotate.view].x,
[rotate locationInView:rotate.view].y);
_mbglMap->setBearing(newDegrees, center);
_mbglMap->setBearing(newDegrees, { centerPoint.x, centerPoint.y });
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1205,7 +1214,7 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
CGFloat newRadians = radians + velocity * duration * 0.1;
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
_mbglMap->setBearing(newDegrees, MGLDurationInSeconds(duration));
_mbglMap->setBearing(newDegrees, { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
[self notifyGestureDidEndWithDrift:YES];
@@ -1279,24 +1288,13 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
}
else if (doubleTap.state == UIGestureRecognizerStateEnded)
{
CGPoint doubleTapPoint = [doubleTap locationInView:doubleTap.view];
CGPoint zoomInPoint = doubleTapPoint;
CGPoint userPoint = [self convertCoordinate:self.userLocation.coordinate toPointToView:self];
CGPoint gesturePoint = [doubleTap locationInView:doubleTap.view];
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
CGRect userLocationRect = CGRectMake(userPoint.x - 40, userPoint.y - 40, 80, 80);
if (CGRectContainsPoint(userLocationRect, doubleTapPoint))
{
zoomInPoint = userPoint;
}
}
if ( ! CGPointEqualToPoint(zoomInPoint, userPoint))
{
self.userTrackingMode = MGLUserTrackingModeNone;
gesturePoint = self.userLocationAnnotationViewCenter;
}
mbgl::PrecisionPoint center(zoomInPoint.x, zoomInPoint.y);
mbgl::PrecisionPoint center(gesturePoint.x, gesturePoint.y);
_mbglMap->scaleBy(2, center, MGLDurationInSeconds(MGLAnimationDuration));
__weak MGLMapView *weakSelf = self;
@@ -1322,20 +1320,13 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
}
else if (twoFingerTap.state == UIGestureRecognizerStateEnded)
{
CGPoint zoomOutPoint;
CGPoint gesturePoint = [twoFingerTap locationInView:twoFingerTap.view];
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
zoomOutPoint = self.center;
}
else
{
self.userTrackingMode = MGLUserTrackingModeNone;
zoomOutPoint = CGPointMake([twoFingerTap locationInView:twoFingerTap.view].x, [twoFingerTap locationInView:twoFingerTap.view].y);
gesturePoint = self.userLocationAnnotationViewCenter;
}
mbgl::PrecisionPoint center(zoomOutPoint.x, zoomOutPoint.y);
mbgl::PrecisionPoint center(gesturePoint.x, gesturePoint.y);
_mbglMap->scaleBy(0.5, center, MGLDurationInSeconds(MGLAnimationDuration));
__weak MGLMapView *weakSelf = self;
@@ -1370,9 +1361,14 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
CGFloat newZoom = log2f(self.scale) + (distance / 75);
if (newZoom < _mbglMap->getMinZoom()) return;
mbgl::PrecisionPoint center(self.bounds.size.width / 2, self.bounds.size.height / 2);
_mbglMap->scaleBy(powf(2, newZoom) / _mbglMap->getScale(), center);
CGPoint centerPoint = self.contentCenter;
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
centerPoint = self.userLocationAnnotationViewCenter;
}
_mbglMap->scaleBy(powf(2, newZoom) / _mbglMap->getScale(),
{ centerPoint.x, centerPoint.y });
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}