summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--include/mbgl/ios/MGLMapView.h16
-rw-r--r--platform/ios/src/MGLMapView.mm65
3 files changed, 76 insertions, 6 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c93d0b2b43..f0b07198c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,7 @@ Known issues:
- Tapping now selects annotations more reliably. Tapping near the top of a large annotation image now selects that annotation. An annotation image’s alignment insets influence how far away the user can tap and still select the annotation. For example, if your annotation image has a large shadow, you can keep that shadow from being tappable by excluding it from the image’s alignment rect. ([#3261](https://github.com/mapbox/mapbox-gl-native/pull/3261))
- A new method on MGLMapView, `-flyToCamera:withDuration:completionHandler:`, lets you transition between viewpoints along an arc as if by aircraft. ([#3171](https://github.com/mapbox/mapbox-gl-native/pull/3171), [#3301](https://github.com/mapbox/mapbox-gl-native/pull/3301))
- 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))
- 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))
- 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))
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
index 569eb00ba4..d2bd849176 100644
--- a/include/mbgl/ios/MGLMapView.h
+++ b/include/mbgl/ios/MGLMapView.h
@@ -574,6 +574,22 @@ IB_DESIGNABLE
*/
- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion;
+/**
+ The distance from the edges of the map view’s frame to the edges of the map
+ view’s logical viewport.
+
+ When the value of this property is equal to `UIEdgeInsetsZero`, viewport
+ properties such as `centerCoordinate` assume a viewport that matches the map
+ view’s frame. Otherwise, those properties are inset, excluding part of the
+ frame from the viewport. For instance, if the only the top edge is inset, the
+ map center is effectively shifted downward.
+
+ When the map view’s superview is an instance of `UIViewController` whose
+ `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this
+ property may be overridden at any time.
+ */
+@property (nonatomic, assign) UIEdgeInsets contentInset;
+
#pragma mark Converting Geographic Coordinates
/**
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index e1d7866d0b..4f1bac781d 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -413,13 +413,15 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
//
mbgl::CameraOptions options;
options.center = mbgl::LatLng(0, 0);
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ options.padding = padding;
options.zoom = _mbglMap->getMinZoom();
_mbglMap->jumpTo(options);
_pendingLatitude = NAN;
_pendingLongitude = NAN;
// metrics: map load event
- mbgl::LatLng latLng = _mbglMap->getLatLng();
+ mbgl::LatLng latLng = _mbglMap->getLatLng(padding);
int zoom = round(_mbglMap->getZoom());
[MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{
@@ -750,6 +752,8 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
- (void)layoutSubviews
{
[super layoutSubviews];
+
+ [self adjustContentInset];
if ( ! _isTargetingInterfaceBuilder)
{
@@ -769,6 +773,49 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
}
}
+/// Updates `contentInset` to reflect the current window geometry.
+- (void)adjustContentInset
+{
+ // We could crawl all the way up the responder chain using
+ // -viewControllerForLayoutGuides, but an intervening view means that any
+ // manual contentInset should not be overridden; something other than the
+ // top and bottom bars may be influencing the manual inset.
+ UIViewController *viewController;
+ if ([self.nextResponder isKindOfClass:[UIViewController class]])
+ {
+ // This map view is the content view of a view controller.
+ viewController = (UIViewController *)self.nextResponder;
+ }
+ else if ([self.superview.nextResponder isKindOfClass:[UIViewController class]])
+ {
+ // This map view is an immediate child of a view controller’s content view.
+ viewController = (UIViewController *)self.superview.nextResponder;
+ }
+
+ if ( ! viewController.automaticallyAdjustsScrollViewInsets)
+ {
+ return;
+ }
+
+ UIEdgeInsets contentInsets = UIEdgeInsetsZero;
+ CGPoint topPoint = CGPointMake(0, viewController.topLayoutGuide.length);
+ contentInsets.top = [self convertPoint:topPoint fromView:viewController.view].y;
+ CGPoint bottomPoint = CGPointMake(0, CGRectGetMaxY(viewController.view.bounds)
+ - viewController.bottomLayoutGuide.length);
+ contentInsets.bottom = (CGRectGetMaxY(self.bounds) - [self convertPoint:bottomPoint
+ fromView:viewController.view].y);
+
+ if ( ! UIEdgeInsetsEqualToEdgeInsets(contentInsets, self.contentInset))
+ {
+ // After adjusting the content insets, move the center coordinate from
+ // the old frame of reference to the new one represented by the newly
+ // set content insets.
+ CLLocationCoordinate2D oldCenter = self.centerCoordinate;
+ self.contentInset = contentInsets;
+ self.centerCoordinate = oldCenter;
+ }
+}
+
#pragma mark - Life Cycle -
- (void)updateFromDisplayLink
@@ -1587,7 +1634,8 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
- (CLLocationCoordinate2D)centerCoordinate
{
- return MGLLocationCoordinate2DFromLatLng(_mbglMap->getLatLng());
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ return MGLLocationCoordinate2DFromLatLng(_mbglMap->getLatLng(padding));
}
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated
@@ -1612,6 +1660,7 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
mbgl::CameraOptions cameraOptions;
cameraOptions.center = MGLLatLngFromLocationCoordinate2D(centerCoordinate);
+ cameraOptions.padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
cameraOptions.zoom = fmaxf(zoomLevel, self.currentMinimumZoom);
if (direction >= 0)
{
@@ -1723,7 +1772,8 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
// NOTE: does not disrupt tracking mode
[self willChangeValueForKey:@"visibleCoordinateBounds"];
- mbgl::EdgeInsets mbglInsets = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
mbgl::AnnotationSegment segment;
segment.reserve(count);
for (NSUInteger i = 0; i < count; i++)
@@ -1731,7 +1781,7 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
segment.push_back({coordinates[i].latitude, coordinates[i].longitude});
}
- mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngs(segment, mbglInsets);
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngs(segment, padding);
if (direction >= 0)
{
cameraOptions.angle = MGLRadiansFromDegrees(-direction);
@@ -1785,7 +1835,9 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
CGFloat duration = animated ? MGLAnimationDuration : 0;
- _mbglMap->setBearing(direction, MGLDurationInSeconds(duration));
+ _mbglMap->setBearing(direction,
+ MGLEdgeInsetsFromNSEdgeInsets(self.contentInset),
+ MGLDurationInSeconds(duration));
}
- (void)setDirection:(CLLocationDirection)direction
@@ -1910,6 +1962,7 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
{
mbgl::CameraOptions options;
options.center = MGLLatLngFromLocationCoordinate2D(camera.centerCoordinate);
+ options.padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
options.zoom = MGLZoomLevelForAltitude(camera.altitude, camera.pitch,
camera.centerCoordinate.latitude,
self.frame.size);
@@ -2971,7 +3024,7 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
{
// center on user location unless we're already centered there (or very close)
//
- CGPoint mapCenterPoint = [self convertPoint:self.center fromView:self.superview];
+ CGPoint mapCenterPoint = [self convertCoordinate:self.centerCoordinate toPointToView:self];
CGPoint userLocationPoint = [self convertCoordinate:self.userLocation.coordinate toPointToView:self];
if (std::abs(userLocationPoint.x - mapCenterPoint.x) > 1.0 || std::abs(userLocationPoint.y - mapCenterPoint.y) > 1.0)