summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Guerra <fabian.guerra@mapbox.com>2019-09-06 20:09:17 -0700
committerFabian Guerra <fabian.guerra@mapbox.com>2019-10-03 10:27:10 -0700
commitae5ed02b72c813877d21cce6bd6e1738f0edcf84 (patch)
treed0a3edd38178dd1dacae89b04103e57de7735317
parent09e089952b3a290c23ff0e2c602b75801709515e (diff)
downloadqtlocation-mapboxgl-ae5ed02b72c813877d21cce6bd6e1738f0edcf84.tar.gz
[ios] Fix an issue that caused the ornaments ignore the contentInsets.
Fixed an issue that caused ornaments ignore the contentInset. Added a new property automaticallyAdjustContentInset that has the same purpose as UIViewController. automaticallyAdjustsScrollViewInsets. This was changed due to the latter being deprecated.
-rw-r--r--platform/ios/src/MGLMapView.h31
-rw-r--r--platform/ios/src/MGLMapView.mm98
-rw-r--r--platform/ios/test/MGLMapViewContentInsetTests.m33
3 files changed, 111 insertions, 51 deletions
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 017ba525c4..1ad7e8f594 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -287,6 +287,19 @@ MGL_EXPORT
- (IBAction)reloadStyle:(nullable id)sender;
/**
+ A boolean value that indicates if whether the map view should automatically
+ adjust its content insets.
+
+ When this property is set to `YES` the map automatically updates its
+ `contentInset` property to account for any area not covered by navigation bars,
+ tab bars, toolbars, and other ancestors that obscure the map view.
+
+ The default value of this property is `YES`.
+
+ */
+@property (assign) BOOL automaticallyAdjustContentInset;
+
+/**
A Boolean value indicating whether the map may display scale information.
The scale bar may not be shown at all zoom levels. The scale bar becomes visible
@@ -1309,9 +1322,9 @@ MGL_EXPORT
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.
+ When the map view’s property `automaticallyAdjustContentInset` is set to `YES`,
+ the value of this property may be overridden at any time. To persist the value
+ set it to `NO`.
Changing the value of this property updates the map view immediately. If you
want to animate the change, use the `-setContentInset:animated:completionHandler:`
@@ -1329,9 +1342,9 @@ MGL_EXPORT
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.
+ When the map view’s property `automaticallyAdjustContentInset` is set to `YES`,
+ the value of this property may be overridden at any time. To persist the value
+ set it to `NO`.
To specify a completion handler to execute after the animation finishes, use
the `-setContentInset:animated:completionHandler:` method.
@@ -1354,9 +1367,9 @@ MGL_EXPORT
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.
+ When the map view’s property `automaticallyAdjustContentInset` is set to `YES`,
+ the value of this property may be overridden at any time. To persist the value
+ set it to `NO`.
@param contentInset The new values to inset the content by.
@param animated Specify `YES` if you want the map view to animate the change to
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 62b943fd3d..6f0257a0d8 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -274,6 +274,10 @@ public:
/// Tilt gesture recognizer helper
@property (nonatomic, assign) CGPoint dragGestureMiddlePoint;
+/// This property is used to keep track of the view's safe edge insets
+/// and calculate the ornament's position
+@property (nonatomic, assign) UIEdgeInsets safeMapViewContentInsets;
+
- (mbgl::Map &)mbglMap;
@end
@@ -518,6 +522,8 @@ public:
_annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary];
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
+
+ _automaticallyAdjustContentInset = YES;
// setup logo
//
@@ -832,23 +838,31 @@ public:
size:(CGSize)size
margins:(CGPoint)margins {
NSMutableArray *updatedConstraints = [NSMutableArray array];
+ UIEdgeInsets inset = UIEdgeInsetsZero;
+
+ if (! self.automaticallyAdjustContentInset) {
+ inset = UIEdgeInsetsMake(self.contentInset.top - self.safeMapViewContentInsets.top,
+ self.contentInset.left - self.safeMapViewContentInsets.left,
+ self.contentInset.bottom - self.safeMapViewContentInsets.bottom,
+ self.contentInset.right - self.safeMapViewContentInsets.right);
+ }
switch (position) {
case MGLOrnamentPositionTopLeft:
- [updatedConstraints addObject:[view.topAnchor constraintEqualToAnchor:self.mgl_safeTopAnchor constant:margins.y]];
- [updatedConstraints addObject:[view.leadingAnchor constraintEqualToAnchor:self.mgl_safeLeadingAnchor constant:margins.x]];
+ [updatedConstraints addObject:[view.topAnchor constraintEqualToAnchor:self.mgl_safeTopAnchor constant:margins.y + inset.top]];
+ [updatedConstraints addObject:[view.leadingAnchor constraintEqualToAnchor:self.mgl_safeLeadingAnchor constant:margins.x + inset.left]];
break;
case MGLOrnamentPositionTopRight:
- [updatedConstraints addObject:[view.topAnchor constraintEqualToAnchor:self.mgl_safeTopAnchor constant:margins.y]];
- [updatedConstraints addObject:[self.mgl_safeTrailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margins.x]];
+ [updatedConstraints addObject:[view.topAnchor constraintEqualToAnchor:self.mgl_safeTopAnchor constant:margins.y + inset.top]];
+ [updatedConstraints addObject:[self.mgl_safeTrailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margins.x + inset.right]];
break;
case MGLOrnamentPositionBottomLeft:
- [updatedConstraints addObject:[self.mgl_safeBottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:margins.y]];
- [updatedConstraints addObject:[view.leadingAnchor constraintEqualToAnchor:self.mgl_safeLeadingAnchor constant:margins.x]];
+ [updatedConstraints addObject:[self.mgl_safeBottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:margins.y + inset.bottom]];
+ [updatedConstraints addObject:[view.leadingAnchor constraintEqualToAnchor:self.mgl_safeLeadingAnchor constant:margins.x + inset.left]];
break;
case MGLOrnamentPositionBottomRight:
- [updatedConstraints addObject:[self.mgl_safeBottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:margins.y]];
- [updatedConstraints addObject: [self.mgl_safeTrailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margins.x]];
+ [updatedConstraints addObject:[self.mgl_safeBottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:margins.y + inset.bottom]];
+ [updatedConstraints addObject: [self.mgl_safeTrailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margins.x + inset.right]];
break;
}
@@ -967,40 +981,44 @@ public:
/// 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]])
+ UIEdgeInsets adjustedContentInsets = UIEdgeInsetsZero;
+
+ if (@available(iOS 11.0, *))
{
- // This map view is an immediate child of a view controller’s content view.
- viewController = (UIViewController *)self.superview.nextResponder;
- }
+ adjustedContentInsets = self.safeAreaInsets;
+
+ } else {
+ // 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;
+ }
+
+ adjustedContentInsets.top = viewController.topLayoutGuide.length;
+ CGFloat bottomPoint = CGRectGetMaxY(viewController.view.bounds) -
+ (CGRectGetMaxY(viewController.view.bounds)
+ - viewController.bottomLayoutGuide.length);
+ adjustedContentInsets.bottom = bottomPoint;
- if ( ! viewController.automaticallyAdjustsScrollViewInsets)
+ }
+
+ self.safeMapViewContentInsets = adjustedContentInsets;
+ if ( ! self.automaticallyAdjustContentInset)
{
return;
}
-
- UIEdgeInsets contentInset = UIEdgeInsetsZero;
- CGPoint topPoint = CGPointMake(0, viewController.topLayoutGuide.length);
- contentInset.top = [self convertPoint:topPoint fromView:viewController.view].y;
- CGPoint bottomPoint = CGPointMake(0, CGRectGetMaxY(viewController.view.bounds)
- - viewController.bottomLayoutGuide.length);
- contentInset.bottom = (CGRectGetMaxY(self.bounds)
- - [self convertPoint:bottomPoint fromView:viewController.view].y);
-
- // Negative insets are invalid, replace with 0.
- contentInset.top = fmaxf(contentInset.top, 0);
- contentInset.bottom = fmaxf(contentInset.bottom, 0);
-
- self.contentInset = contentInset;
+
+ self.contentInset = adjustedContentInsets;
}
- (void)setContentInset:(UIEdgeInsets)contentInset
@@ -1015,6 +1033,12 @@ public:
- (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion
{
+ // makes sure the insets don't have negative values that could hide the ornaments
+ // thus violating our ToS
+ contentInset = UIEdgeInsetsMake(fmaxf(contentInset.top, 0),
+ fmaxf(contentInset.left, 0),
+ fmaxf(contentInset.bottom, 0),
+ fmaxf(contentInset.right, 0));
MGLLogDebug(@"Setting contentInset: %@ animated:", NSStringFromUIEdgeInsets(contentInset), MGLStringFromBOOL(animated));
if (UIEdgeInsetsEqualToEdgeInsets(contentInset, self.contentInset))
{
diff --git a/platform/ios/test/MGLMapViewContentInsetTests.m b/platform/ios/test/MGLMapViewContentInsetTests.m
index aa6f57cc0a..6ec669d847 100644
--- a/platform/ios/test/MGLMapViewContentInsetTests.m
+++ b/platform/ios/test/MGLMapViewContentInsetTests.m
@@ -5,6 +5,7 @@
@property (nonatomic) MGLMapView *mapView;
@property (nonatomic) UIWindow *window;
+@property (nonatomic) UIViewController *viewController;
@property (nonatomic) XCTestExpectation *styleLoadingExpectation;
@property (assign) CGRect screenBounds;
@@ -22,11 +23,11 @@
self.mapView.zoomLevel = 16;
self.mapView.delegate = self;
-
- UIView *view = [[UIView alloc] initWithFrame:self.screenBounds];
- [view addSubview:self.mapView];
+ self.viewController = [[UIViewController alloc] init];
+ self.viewController.view = [[UIView alloc] initWithFrame:self.screenBounds];
+ [self.viewController.view addSubview:self.mapView];
self.window = [[UIWindow alloc] initWithFrame:self.screenBounds];
- [self.window addSubview:view];
+ [self.window addSubview:self.viewController.view];
[self.window makeKeyAndVisible];
if (!self.mapView.style) {
@@ -99,7 +100,7 @@
XCTAssertTrue(CGPointEqualToPoint(attributionView.frame.origin, expectedAttributionOrigin));
UIEdgeInsets insets = UIEdgeInsetsMake(15, 10, 20, 5);
- self.mapView.automaticallyAdjustContentInset = YES;
+ self.mapView.automaticallyAdjustContentInset = NO;
self.mapView.contentInset = insets;
[self.mapView setNeedsLayout];
@@ -121,6 +122,28 @@
expectedAttributionOrigin = CGPointMake(x, y);
XCTAssertTrue(CGPointEqualToPoint(attributionView.frame.origin, expectedAttributionOrigin));
+ // tests that passing negative values result in a 0 inset value
+ insets = UIEdgeInsetsMake(-100, -100, -100, -100);
+ self.mapView.contentInset = insets;
+
+ [self.mapView setNeedsLayout];
+ [self.mapView layoutIfNeeded];
+
+ expectedScaleBarOrigin = CGPointMake(margin, margin);
+ XCTAssertTrue(CGPointEqualToPoint(scaleBar.frame.origin, expectedScaleBarOrigin));
+
+ x = self.screenBounds.size.width - compassView.bounds.size.width - margin;
+ expectedCompassOrigin = CGPointMake(x, margin);
+ XCTAssertTrue(CGPointEqualToPoint(compassView.frame.origin, expectedCompassOrigin));
+
+ y = self.screenBounds.size.height - logoView.bounds.size.height - margin;
+ expectedLogoOrigin = CGPointMake(margin, y);
+ XCTAssertTrue(CGPointEqualToPoint(logoView.frame.origin, expectedLogoOrigin));
+
+ x = self.screenBounds.size.width - attributionView.bounds.size.width - margin;
+ y = self.screenBounds.size.height - attributionView.bounds.size.height - margin;
+ expectedAttributionOrigin = CGPointMake(x, y);
+ XCTAssertTrue(CGPointEqualToPoint(attributionView.frame.origin, expectedAttributionOrigin));
}
@end