summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLloyd Sheng <i@lloydsheng.com>2019-03-20 15:41:56 +0800
committerGitHub <noreply@github.com>2019-03-20 15:41:56 +0800
commitdab3ab79ad54194590a82231cea55940e377e551 (patch)
tree0f6e3f96746e5765d39e8fc411262f85109971c5
parentcd0ea372ea06f2f43577644c033c8a974801f3b9 (diff)
downloadqtlocation-mapboxgl-dab3ab79ad54194590a82231cea55940e377e551.tar.gz
[ios] Cherry-pick PR #13911 to adding options to customize positions of mapview ornaments (#13863)
* Cherry-pick PR #13556 to adding options to customize positions of mapbox ornaments
-rw-r--r--platform/ios/CHANGELOG.md3
-rw-r--r--platform/ios/app/MBXOrnamentsViewController.h5
-rw-r--r--platform/ios/app/MBXOrnamentsViewController.m94
-rw-r--r--platform/ios/app/MBXViewController.m11
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj6
-rw-r--r--platform/ios/src/MGLMapView.h58
-rw-r--r--platform/ios/src/MGLMapView.mm469
-rw-r--r--platform/ios/src/MGLScaleBar.mm25
-rw-r--r--platform/ios/test/MGLMapViewLayoutTests.m139
-rw-r--r--platform/ios/test/MGLMapViewScaleBarTests.m4
10 files changed, 608 insertions, 206 deletions
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index ba8ea3e2d4..eead0b5621 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -5,6 +5,9 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* `MGLMapSnapshotter` now follows "MGLIdeographicFontFamilyName" app setting to reduce font data usage while snapshotting CJK maps [#13427](https://github.com/mapbox/mapbox-gl-native/pull/13427)
+## v4.5.0-cn.2
+* Added options to customize position of scale bar, compass, logo and attribution. ([#13863](https://github.com/mapbox/mapbox-gl-native/pull/13863))
+
## v4.5.0-cn.1
* Restored iOS 8 support for the Mapbox Maps SDK for iOS. ([#13036](https://github.com/mapbox/mapbox-gl-native/pull/13036))
diff --git a/platform/ios/app/MBXOrnamentsViewController.h b/platform/ios/app/MBXOrnamentsViewController.h
new file mode 100644
index 0000000000..087101cdc0
--- /dev/null
+++ b/platform/ios/app/MBXOrnamentsViewController.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+@interface MBXOrnamentsViewController : UIViewController
+
+@end
diff --git a/platform/ios/app/MBXOrnamentsViewController.m b/platform/ios/app/MBXOrnamentsViewController.m
new file mode 100644
index 0000000000..45288af0ab
--- /dev/null
+++ b/platform/ios/app/MBXOrnamentsViewController.m
@@ -0,0 +1,94 @@
+#import "MBXOrnamentsViewController.h"
+
+@import Mapbox;
+
+@interface MBXOrnamentsViewController ()<MGLMapViewDelegate>
+
+@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic) NSTimer *timer;
+@property (nonatomic) NSInteger currentPositionIndex;
+
+@end
+
+@implementation MBXOrnamentsViewController
+
+- (void)setCurrentPositionIndex:(NSInteger)currentPositionIndex {
+ MGLOrnamentPosition ornamentPositions[5][4] = {
+ {
+ MGLOrnamentPositionTopLeft,
+ MGLOrnamentPositionTopRight,
+ MGLOrnamentPositionBottomRight,
+ MGLOrnamentPositionBottomLeft
+ },
+ {
+ MGLOrnamentPositionTopRight,
+ MGLOrnamentPositionBottomRight,
+ MGLOrnamentPositionBottomLeft,
+ MGLOrnamentPositionTopLeft
+ },
+ {
+ MGLOrnamentPositionBottomRight,
+ MGLOrnamentPositionBottomLeft,
+ MGLOrnamentPositionTopLeft,
+ MGLOrnamentPositionTopRight
+ },
+ {
+ MGLOrnamentPositionBottomLeft,
+ MGLOrnamentPositionTopLeft,
+ MGLOrnamentPositionTopRight,
+ MGLOrnamentPositionBottomRight
+ },
+ {
+ MGLOrnamentPositionTopLeft,
+ MGLOrnamentPositionTopRight,
+ MGLOrnamentPositionBottomRight,
+ MGLOrnamentPositionBottomLeft
+ }
+ };
+ MGLOrnamentPosition *currentPosition = ornamentPositions[currentPositionIndex];
+ self.mapView.scaleBarPosition = currentPosition[0];
+ self.mapView.compassViewPosition = currentPosition[1];
+ self.mapView.logoViewPosition = currentPosition[2];
+ self.mapView.attributionButtonPosition = currentPosition[3];
+
+ _currentPositionIndex = currentPositionIndex;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ self.title = @"Ornaments";
+
+ MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:self.view.frame];
+ mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [mapView setCenterCoordinate:CLLocationCoordinate2DMake(39.915143, 116.404053)
+ zoomLevel:16
+ direction:30
+ animated:NO];
+ mapView.delegate = self;
+ mapView.showsScale = YES;
+ [self.view addSubview:mapView];
+
+ self.mapView = mapView;
+}
+
+- (void)viewDidDisappear:(BOOL)animated {
+ [self.timer invalidate];
+ self.timer = nil;
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ self.timer = [NSTimer scheduledTimerWithTimeInterval:1
+ target:self
+ selector:@selector(onTimerTick)
+ userInfo:nil
+ repeats:YES];
+}
+
+- (void)onTimerTick {
+ self.currentPositionIndex ++;
+ if (self.currentPositionIndex >= 4) {
+ self.currentPositionIndex = 0;
+ }
+}
+
+@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 42bfd01d42..370369067d 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -7,6 +7,7 @@
#import "MBXUserLocationAnnotationView.h"
#import "LimeGreenStyleLayer.h"
#import "MBXEmbeddedMapViewController.h"
+#import "MBXOrnamentsViewController.h"
#import <Mapbox/Mapbox.h>
#import "../src/MGLMapView_Experimental.h"
@@ -100,8 +101,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousShouldLimitCameraChanges,
MBXSettingsMiscellaneousShowCustomLocationManager,
+ MBXSettingsMiscellaneousOrnamentsPlacement,
MBXSettingsMiscellaneousPrintLogFile,
- MBXSettingsMiscellaneousDeleteLogFile,
+ MBXSettingsMiscellaneousDeleteLogFile
};
// Utility methods
@@ -462,6 +464,7 @@ CLLocationCoordinate2D randomWorldCoordinate() {
@"Show Snapshots",
[NSString stringWithFormat:@"%@ Camera Changes", (_shouldLimitCameraChanges ? @"Unlimit" : @"Limit")],
@"View Route Simulation",
+ @"Ornaments Placement",
]];
if (self.debugLoggingEnabled)
@@ -713,6 +716,12 @@ CLLocationCoordinate2D randomWorldCoordinate() {
}
break;
}
+ case MBXSettingsMiscellaneousOrnamentsPlacement:
+ {
+ MBXOrnamentsViewController *ornamentsViewController = [[MBXOrnamentsViewController alloc] init];
+ [self.navigationController pushViewController:ornamentsViewController animated:YES];
+ break;
+ }
default:
NSAssert(NO, @"All miscellaneous setting rows should be implemented");
break;
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 76f09e0948..903a8ffa49 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -294,6 +294,7 @@
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */; };
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */; };
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; };
+ 6FA9341721EF372100AA9CA8 /* MBXOrnamentsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA9341521EF372100AA9CA8 /* MBXOrnamentsViewController.m */; };
8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; };
@@ -976,6 +977,8 @@
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXEmbeddedMapViewController.h; sourceTree = "<group>"; };
632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXEmbeddedMapViewController.m; sourceTree = "<group>"; };
6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; };
+ 6FA9341521EF372100AA9CA8 /* MBXOrnamentsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXOrnamentsViewController.m; sourceTree = "<group>"; };
+ 6FA9341621EF372100AA9CA8 /* MBXOrnamentsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXOrnamentsViewController.h; sourceTree = "<group>"; };
8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; };
8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; };
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; };
@@ -1806,6 +1809,8 @@
DA1DC99A1CB6E064006E619F /* MBXViewController.m */,
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */,
632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */,
+ 6FA9341621EF372100AA9CA8 /* MBXOrnamentsViewController.h */,
+ 6FA9341521EF372100AA9CA8 /* MBXOrnamentsViewController.m */,
DA821D051CCC6D59007508D4 /* Main.storyboard */,
DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */,
DA1DC99E1CB6E088006E619F /* Assets.xcassets */,
@@ -2852,6 +2857,7 @@
DA1DC9971CB6E046006E619F /* main.m in Sources */,
354B839C1D2E9B48005D9406 /* MBXUserLocationAnnotationView.m in Sources */,
DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */,
+ 6FA9341721EF372100AA9CA8 /* MBXOrnamentsViewController.m in Sources */,
DA1DC96B1CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m in Sources */,
DA1DC96A1CB6C6B7006E619F /* MBXCustomCalloutView.m in Sources */,
927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */,
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 5312804cd2..3678f755ee 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -49,6 +49,24 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
};
/**
+ The position of scale bar, compass, logo and attribution in a map view. Used with
+ `MGLMapView.scaleBarPosition`,
+ `MGLMapView.compassViewPosition`,
+ `MGLMapView.logoViewPosition`,
+ `MGLMapView.attributionButtonPosition`.
+ */
+typedef NS_ENUM(NSUInteger, MGLOrnamentPosition) {
+ /** Place the ornament in the top left of the map view. */
+ MGLOrnamentPositionTopLeft = 0,
+ /** Place the ornament in the top right of the map view. */
+ MGLOrnamentPositionTopRight,
+ /** Place the ornament in the bottom left of the map view. */
+ MGLOrnamentPositionBottomLeft,
+ /** Place the ornament in the bottom right of the map view. */
+ MGLOrnamentPositionBottomRight,
+};
+
+/**
The mode used to track the user location on the map. Used with
`MGLMapView.userTrackingMode`.
*/
@@ -258,12 +276,32 @@ MGL_EXPORT IB_DESIGNABLE
@property (nonatomic, readonly) UIView *scaleBar;
/**
+ The position of the scale bar. The default value is `MGLOrnamentPositionTopLeft`.
+ */
+@property (nonatomic, assign) MGLOrnamentPosition scaleBarPosition;
+
+/**
+ A `CGPoint` indicating the position offset of the scale bar.
+ */
+@property (nonatomic, assign) CGPoint scaleBarMargins;
+
+/**
A control indicating the map’s direction and allowing the user to manipulate
the direction, positioned in the upper-right corner.
*/
@property (nonatomic, readonly) UIImageView *compassView;
/**
+ The position of the compass view. The default value is `MGLOrnamentPositionTopRight`.
+ */
+@property (nonatomic, assign) MGLOrnamentPosition compassViewPosition;
+
+/**
+ A `CGPoint` indicating the position offset of the compass.
+ */
+@property (nonatomic, assign) CGPoint compassViewMargins;
+
+/**
The Mapbox logo, positioned in the lower-left corner.
@note The Mapbox terms of service, which governs the use of Mapbox-hosted
@@ -275,6 +313,16 @@ MGL_EXPORT IB_DESIGNABLE
@property (nonatomic, readonly) UIImageView *logoView;
/**
+ The position of the logo view. The default value is `MGLOrnamentPositionBottomLeft`.
+ */
+@property (nonatomic, assign) MGLOrnamentPosition logoViewPosition;
+
+/**
+ A `CGPoint` indicating the position offset of the logo.
+ */
+@property (nonatomic, assign) CGPoint logoViewMargins;
+
+/**
A view showing legally required copyright notices and telemetry settings,
positioned at the bottom-right of the map view.
@@ -300,6 +348,16 @@ MGL_EXPORT IB_DESIGNABLE
@property (nonatomic, readonly) UIButton *attributionButton;
/**
+ The position of the attribution button. The default value is `MGLOrnamentPositionBottomRight`.
+ */
+@property (nonatomic, assign) MGLOrnamentPosition attributionButtonPosition;
+
+/**
+ A `CGPoint` indicating the position offset of the attribution.
+ */
+@property (nonatomic, assign) CGPoint attributionButtonMargins;
+
+/**
Show the attribution and telemetry action sheet.
This action is performed when the user taps on the attribution button provided
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index a65ece1c6b..3a786e54d0 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -96,6 +96,8 @@ const MGLExceptionName MGLMissingLocationServicesUsageDescriptionException = @"M
const MGLExceptionName MGLUserLocationAnnotationTypeException = @"MGLUserLocationAnnotationTypeException";
const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundException";
+const CGPoint MGLOrnamentDefaultPositionOffset = CGPointMake(8, 8);
+
/// Indicates the manner in which the map view is tracking the user location.
typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {
/// The map view is not yet tracking the user location.
@@ -479,6 +481,8 @@ public:
_logoView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_logoView];
_logoViewConstraints = [NSMutableArray array];
+ _logoViewPosition = MGLOrnamentPositionBottomLeft;
+ _logoViewMargins = MGLOrnamentDefaultPositionOffset;
// setup attribution
//
@@ -493,6 +497,8 @@ public:
UILongPressGestureRecognizer *attributionLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showAttribution:)];
[_attributionButton addGestureRecognizer:attributionLongPress];
+ _attributionButtonPosition = MGLOrnamentPositionBottomRight;
+ _attributionButtonMargins = MGLOrnamentDefaultPositionOffset;
// setup compass
//
@@ -506,6 +512,8 @@ public:
_compassView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_compassView];
_compassViewConstraints = [NSMutableArray array];
+ _compassViewPosition = MGLOrnamentPositionTopRight;
+ _compassViewMargins = MGLOrnamentDefaultPositionOffset;
// setup scale control
//
@@ -513,7 +521,11 @@ public:
_scaleBar.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_scaleBar];
_scaleBarConstraints = [NSMutableArray array];
-
+ _scaleBarPosition = MGLOrnamentPositionTopLeft;
+ _scaleBarMargins = MGLOrnamentDefaultPositionOffset;
+
+ [self installConstraints];
+
// setup interaction
//
_pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
@@ -757,6 +769,46 @@ public:
return YES;
}
+- (void)setScaleBarPosition:(MGLOrnamentPosition)scaleBarPosition {
+ _scaleBarPosition = scaleBarPosition;
+ [self installScaleBarConstraints];
+}
+
+- (void)setScaleBarMargins:(CGPoint)scaleBarOffset {
+ _scaleBarMargins = scaleBarOffset;
+ [self installScaleBarConstraints];
+}
+
+- (void)setCompassViewPosition:(MGLOrnamentPosition)compassViewPosition {
+ _compassViewPosition = compassViewPosition;
+ [self installCompassViewConstraints];
+}
+
+- (void)setCompassViewMargins:(CGPoint)compassViewOffset {
+ _compassViewMargins = compassViewOffset;
+ [self installCompassViewConstraints];
+}
+
+- (void)setLogoViewPosition:(MGLOrnamentPosition)logoViewPosition {
+ _logoViewPosition = logoViewPosition;
+ [self installLogoViewConstraints];
+}
+
+- (void)setLogoViewMargins:(CGPoint)logoViewOffset {
+ _logoViewMargins = logoViewOffset;
+ [self installLogoViewConstraints];
+}
+
+- (void)setAttributionButtonPosition:(MGLOrnamentPosition)attributionButtonPosition {
+ _attributionButtonPosition = attributionButtonPosition;
+ [self installAttributionButtonConstraints];
+}
+
+- (void)setAttributionButtonMargins:(CGPoint)attributionButtonOffset {
+ _attributionButtonMargins = attributionButtonOffset;
+ [self installAttributionButtonConstraints];
+}
+
- (UIViewController *)viewControllerForLayoutGuides
{
// Per -[UIResponder nextResponder] documentation, a UIView’s next responder
@@ -775,204 +827,239 @@ public:
return nil;
}
-- (void)updateConstraintsPreiOS11 {
- // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets
- // is set to YES, use its view as the parent for constraints. -[MGLMapView adjustContentInset]
- // already take top and bottom layout guides into account. If we don't have a reference, apply
- // constraints against ourself to maintain placement of the subviews.
- //
- UIViewController *viewController = self.viewControllerForLayoutGuides;
- BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
- UIView *containerView = useLayoutGuides ? viewController.view : self;
-
- // compass view
- //
- [containerView removeConstraints:self.compassViewConstraints];
- [self.compassViewConstraints removeAllObjects];
-
- if (useLayoutGuides) {
- [self.compassViewConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self.compassView
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:viewController.topLayoutGuide
- attribute:NSLayoutAttributeBottom
- multiplier:1.0
- constant:8.0]];
- }
- [self.compassViewConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self.compassView
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:self
- attribute:NSLayoutAttributeTop
- multiplier:1.0
- constant:8.0 + self.contentInset.top]];
-
- [self.compassViewConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self
- attribute:NSLayoutAttributeTrailing
- relatedBy:NSLayoutRelationEqual
- toItem:self.compassView
- attribute:NSLayoutAttributeTrailing
- multiplier:1.0
- constant:8.0 + self.contentInset.right]];
+- (void)updateConstraintsForOrnament:(UIView *)view
+ constraints:(NSMutableArray *)constraints
+ position:(MGLOrnamentPosition)position
+ size:(CGSize)size
+ offset:(CGPoint)offset {
+ NSMutableArray *updatedConstraints = [NSMutableArray array];
- [containerView addConstraints:self.compassViewConstraints];
-
- // scale bar view
- //
- [containerView removeConstraints:self.scaleBarConstraints];
- [self.scaleBarConstraints removeAllObjects];
-
- if (useLayoutGuides) {
- [self.scaleBarConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self.scaleBar
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:viewController.topLayoutGuide
- attribute:NSLayoutAttributeBottom
+ if (@available(iOS 11.0, *)) {
+ UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
+
+ switch (position) {
+ case MGLOrnamentPositionTopLeft:
+ [updatedConstraints addObject:[view.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor constant:offset.y]];
+ [updatedConstraints addObject:[view.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor constant:offset.x]];
+ break;
+ case MGLOrnamentPositionTopRight:
+ [updatedConstraints addObject:[view.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor constant:offset.y]];
+ [updatedConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:offset.x]];
+ break;
+ case MGLOrnamentPositionBottomLeft:
+ [updatedConstraints addObject:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:offset.y]];
+ [updatedConstraints addObject:[view.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor constant:offset.x]];
+ break;
+ case MGLOrnamentPositionBottomRight:
+ [updatedConstraints addObject:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:offset.y]];
+ [updatedConstraints addObject: [safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:offset.x]];
+ break;
+
+ default:
+ break;
+ }
+ [updatedConstraints addObject:[view.widthAnchor constraintEqualToConstant:size.width]];
+ [updatedConstraints addObject:[view.heightAnchor constraintEqualToConstant:size.height]];
+ } else {
+ // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets
+ // is set to YES, use its view as the parent for constraints. -[MGLMapView adjustContentInset]
+ // already take top and bottom layout guides into account. If we don't have a reference, apply
+ // constraints against ourself to maintain placement of the subviews.
+ //
+ UIViewController *viewController = self.viewControllerForLayoutGuides;
+ BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
+
+ switch (position) {
+ case MGLOrnamentPositionTopLeft:
+ if (useLayoutGuides) {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1.0
+ constant:offset.y]];
+ }
+ else {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1.0
+ constant:offset.y]];
+ }
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeLeft
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeft
+ multiplier:1.0
+ constant:offset.x]];
+ break;
+ case MGLOrnamentPositionTopRight:
+ if (useLayoutGuides) {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1.0
+ constant:offset.y]];
+ }
+ else {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1.0
+ constant:offset.y]];
+ }
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:view
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1.0
+ constant:offset.x]];
+ break;
+ case MGLOrnamentPositionBottomLeft:
+ if (useLayoutGuides) {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:view
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1.0
+ constant:offset.y]];
+ }
+ else {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:view
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:offset.y]];
+ }
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeLeading
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeading
+ multiplier:1.0
+ constant:offset.x]];
+ break;
+ case MGLOrnamentPositionBottomRight:
+ if (useLayoutGuides) {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:view
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:offset.y]];
+ }
+ else {
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:view
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:offset.y]];
+ }
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:view
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1
+ constant:offset.x]];
+ break;
+
+ default:
+ break;
+ }
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
- constant:8.0]];
- }
- [self.scaleBarConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self.scaleBar
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:self
- attribute:NSLayoutAttributeTop
- multiplier:1.0
- constant:8.0 + self.contentInset.top]];
- [self.scaleBarConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self.scaleBar
- attribute:NSLayoutAttributeLeft
- relatedBy:NSLayoutRelationEqual
- toItem:self
- attribute:NSLayoutAttributeLeft
- multiplier:1.0
- constant:8.0 + self.contentInset.left]];
-
- [containerView addConstraints:self.scaleBarConstraints];
-
- // logo view
- //
- [containerView removeConstraints:self.logoViewConstraints];
- [self.logoViewConstraints removeAllObjects];
-
- if (useLayoutGuides) {
- [self.logoViewConstraints addObject:
- [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:self.logoView
- attribute:NSLayoutAttributeBaseline
+ constant:size.width]];
+ [updatedConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:view
+ attribute:NSLayoutAttributeHeight
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
- constant:8.0 + self.contentInset.bottom]];
+ constant:size.height]];
}
- [self.logoViewConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:self.logoView
- attribute:NSLayoutAttributeBaseline
- multiplier:1
- constant:8.0 + self.contentInset.bottom]];
- [self.logoViewConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self.logoView
- attribute:NSLayoutAttributeLeading
- relatedBy:NSLayoutRelationEqual
- toItem:self
- attribute:NSLayoutAttributeLeading
- multiplier:1.0
- constant:8.0 + self.contentInset.left]];
- [containerView addConstraints:self.logoViewConstraints];
-
- // attribution button
- //
- [containerView removeConstraints:self.attributionButtonConstraints];
- [self.attributionButtonConstraints removeAllObjects];
-
- if (useLayoutGuides) {
- [self.attributionButtonConstraints addObject:
- [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:self.attributionButton
- attribute:NSLayoutAttributeBaseline
- multiplier:1
- constant:8.0 + self.contentInset.bottom]];
- }
- [self.attributionButtonConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationGreaterThanOrEqual
- toItem:self.attributionButton
- attribute:NSLayoutAttributeBaseline
- multiplier:1
- constant:8.0 + self.contentInset.bottom]];
-
- [self.attributionButtonConstraints addObject:
- [NSLayoutConstraint constraintWithItem:self
- attribute:NSLayoutAttributeTrailing
- relatedBy:NSLayoutRelationEqual
- toItem:self.attributionButton
- attribute:NSLayoutAttributeTrailing
- multiplier:1
- constant:8.0 + self.contentInset.right]];
- [containerView addConstraints:self.attributionButtonConstraints];
+
+ [NSLayoutConstraint deactivateConstraints:constraints];
+ [constraints removeAllObjects];
+ [NSLayoutConstraint activateConstraints:updatedConstraints];
+ [constraints addObjectsFromArray:updatedConstraints];
}
-- (void)updateConstraints
+- (void)installConstraints
{
- // If safeAreaLayoutGuide API exists
- if (@available(iOS 11.0, *)) {
- UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
+ [self installCompassViewConstraints];
+ [self installScaleBarConstraints];
+ [self installLogoViewConstraints];
+ [self installAttributionButtonConstraints];
+}
- // compass view
- [self removeConstraints:self.compassViewConstraints];
- [self.compassViewConstraints removeAllObjects];
- [self.compassViewConstraints addObject:[self constraintForYAxisAnchor:self.compassView.topAnchor belowAnchor:safeAreaLayoutGuide.topAnchor]];
- [self.compassViewConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.compassView.rightAnchor
- constant:8.0 + self.contentInset.right]];
- [self addConstraints:self.compassViewConstraints];
-
- // scale bar view
- [self removeConstraints:self.scaleBarConstraints];
- [self.scaleBarConstraints removeAllObjects];
- [self.scaleBarConstraints addObject:[self constraintForYAxisAnchor:self.scaleBar.topAnchor belowAnchor:safeAreaLayoutGuide.topAnchor]];
- [self.scaleBarConstraints addObject:[self.scaleBar.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor
- constant:8.0 + self.contentInset.left]];
- [self addConstraints:self.scaleBarConstraints];
-
- // logo view
- [self removeConstraints:self.logoViewConstraints];
- [self.logoViewConstraints removeAllObjects];
- [self.logoViewConstraints addObject:[self constraintForYAxisAnchor:safeAreaLayoutGuide.bottomAnchor belowAnchor:self.logoView.bottomAnchor]];
- [self.logoViewConstraints addObject:[self.logoView.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor
- constant:8.0 + self.contentInset.left]];
- [self addConstraints:self.logoViewConstraints];
-
- // attribution button
- [self removeConstraints:self.attributionButtonConstraints];
- [self.attributionButtonConstraints removeAllObjects];
- [self.attributionButtonConstraints addObject:[self constraintForYAxisAnchor:safeAreaLayoutGuide.bottomAnchor belowAnchor:self.attributionButton.bottomAnchor]];
- [self.attributionButtonConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.attributionButton.rightAnchor
- constant:8.0 + self.contentInset.right]];
- [self addConstraints:self.attributionButtonConstraints];
- } else {
- [self updateConstraintsPreiOS11];
- }
-
- [super updateConstraints];
+- (void)installCompassViewConstraints {
+ // compass view
+ [self updateConstraintsForOrnament:self.compassView
+ constraints:self.compassViewConstraints
+ position:self.compassViewPosition
+ size:self.compassView.bounds.size
+ offset:self.compassViewMargins];
}
-- (NSLayoutConstraint *)constraintForYAxisAnchor:(NSLayoutYAxisAnchor *)yAxisAnchor belowAnchor:(NSLayoutYAxisAnchor *)anchor
-{
- if (@available(iOS 11.0, *)) {
- return [yAxisAnchor constraintEqualToSystemSpacingBelowAnchor:anchor multiplier:1];
- } else {
- return nil;
- }
+- (void)installScaleBarConstraints {
+ // scale bar view
+ [self updateConstraintsForOrnament:self.scaleBar
+ constraints:self.scaleBarConstraints
+ position:self.scaleBarPosition
+ size:self.scaleBar.intrinsicContentSize
+ offset:self.scaleBarMargins];
+}
+
+- (void)installLogoViewConstraints {
+ // logo view
+ [self updateConstraintsForOrnament:self.logoView
+ constraints:self.logoViewConstraints
+ position:self.logoViewPosition
+ size:self.logoView.bounds.size
+ offset:self.logoViewMargins];
+}
+
+- (void)installAttributionButtonConstraints {
+ // attribution button
+ [self updateConstraintsForOrnament:self.attributionButton
+ constraints:self.attributionButtonConstraints
+ position:self.attributionButtonPosition
+ size:self.attributionButton.bounds.size
+ offset:self.attributionButtonMargins];
}
- (BOOL)isOpaque
@@ -1095,7 +1182,7 @@ public:
}
// Compass, logo and attribution button constraints needs to be updated.
- [self setNeedsUpdateConstraints];
+ [self installConstraints];
}
/// Returns the frame of inset content within the map view.
@@ -1249,6 +1336,7 @@ public:
- (void)didMoveToSuperview
{
[self validateDisplayLink];
+ [self installConstraints];
[super didMoveToSuperview];
}
@@ -6085,6 +6173,7 @@ public:
if ( ! self.scaleBar.hidden)
{
[(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]];
+ [self installScaleBarConstraints];
}
}
diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm
index a180280adc..c39fce8ce6 100644
--- a/platform/ios/src/MGLScaleBar.mm
+++ b/platform/ios/src/MGLScaleBar.mm
@@ -86,7 +86,7 @@ static const MGLRow MGLImperialTable[] ={
@property (nonatomic, assign) CGFloat borderWidth;
@property (nonatomic) NSCache* labelImageCache;
@property (nonatomic) MGLScaleBarLabel* prototypeLabel;
-
+@property (nonatomic) CGFloat lastLabelWidth;
@end
@@ -94,6 +94,7 @@ static const CGFloat MGLBarHeight = 4;
static const CGFloat MGLFeetPerMeter = 3.28084;
@interface MGLScaleBarLabel : UILabel
+
@end
@implementation MGLScaleBarLabel
@@ -184,7 +185,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
#pragma mark - Dimensions
- (CGSize)intrinsicContentSize {
- return CGSizeMake(self.actualWidth, 16);
+ return self.actualWidth > 0 ? CGSizeMake(ceil(self.actualWidth + self.lastLabelWidth/2), 16) : CGSizeZero;
}
- (CGFloat)actualWidth {
@@ -194,8 +195,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
- (CGFloat)maximumWidth {
CGFloat fullWidth = CGRectGetWidth(self.superview.bounds);
- CGFloat padding = [self usesRightToLeftLayout] ? fullWidth - CGRectGetMaxX(self.frame) : CGRectGetMinX(self.frame);
- return floorf(fullWidth / 2 - padding);
+ return floorf(fullWidth / 2);
}
- (CGFloat)unitsPerPoint {
@@ -261,11 +261,8 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
self.row = [self preferredRow];
- CGSize size = self.intrinsicContentSize;
- self.frame = CGRectMake(CGRectGetMinX(self.frame),
- CGRectGetMinY(self.frame),
- size.width,
- size.height);
+ [self invalidateIntrinsicContentSize];
+ [self setNeedsLayout];
}
- (void)updateVisibility {
@@ -375,7 +372,9 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
CLLocationDistance barDistance = multiplier * i;
UIImage *image = [self cachedLabelImageForDistance:barDistance];
-
+ if (i == self.row.numberOfBars) {
+ self.lastLabelWidth = image.size.width;
+ }
labelView.layer.contents = (id)image.CGImage;
labelView.layer.contentsScale = image.scale;
}
@@ -413,7 +412,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
self.containerView.frame = CGRectMake(CGRectGetMinX(self.bars.firstObject.frame),
- CGRectGetMaxY(self.bounds)-MGLBarHeight,
+ self.intrinsicContentSize.height-MGLBarHeight,
self.actualWidth,
MGLBarHeight+self.borderWidth*2);
@@ -425,12 +424,12 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
- (void)layoutLabels {
- CGFloat barWidth = round(self.bounds.size.width / self.bars.count);
+ CGFloat barWidth = round(self.actualWidth / self.bars.count);
BOOL RTL = [self usesRightToLeftLayout];
NSUInteger i = RTL ? self.bars.count : 0;
for (UIView *label in self.labelViews) {
CGFloat xPosition = round(barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth);
- CGFloat yPosition = round(0.5 * (CGRectGetMaxY(self.bounds) - MGLBarHeight));
+ CGFloat yPosition = round(0.5 * (self.intrinsicContentSize.height - MGLBarHeight));
CGRect frame = label.frame;
frame.origin.x = xPosition;
diff --git a/platform/ios/test/MGLMapViewLayoutTests.m b/platform/ios/test/MGLMapViewLayoutTests.m
index 4bdff51e25..d902665768 100644
--- a/platform/ios/test/MGLMapViewLayoutTests.m
+++ b/platform/ios/test/MGLMapViewLayoutTests.m
@@ -4,6 +4,27 @@
#import "MGLAccountManager.h"
+@interface MGLOrnamentTestData : NSObject
+
+@property (nonatomic) MGLOrnamentPosition position;
+@property (nonatomic) CGPoint margins;
+@property (nonatomic) CGPoint expectedOrigin;
+
+@end
+
+@implementation MGLOrnamentTestData
+
++ (instancetype)createWithPostion:(MGLOrnamentPosition)position offset:(CGPoint)offset expectedOrigin:(CGPoint)expectedOrigin {
+ MGLOrnamentTestData *data = [[MGLOrnamentTestData alloc] init];
+ data.position = position;
+ data.margins = offset;
+ data.expectedOrigin = expectedOrigin;
+ return data;
+}
+
+@end
+
+
@interface MGLMapViewLayoutTests : XCTestCase<MGLMapViewDelegate>
@property (nonatomic) UIView *superView;
@@ -106,4 +127,122 @@
XCTAssertEqualWithAccuracy(CGRectGetMinY(logoView.frame), expectedLogoOriginY, accuracy);
}
+- (NSArray *)makeTestDataListWithView:(UIView *)view margin:(CGFloat)margin {
+ CGFloat bottomSafeAreaInset = 0.0;
+ if (@available(iOS 11.0, *)) {
+ bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom;
+ }
+
+ return @[
+ [MGLOrnamentTestData createWithPostion:MGLOrnamentPositionTopLeft
+ offset:CGPointMake(margin, margin)
+ expectedOrigin:CGPointMake(margin, margin)],
+ [MGLOrnamentTestData createWithPostion:MGLOrnamentPositionTopRight
+ offset:CGPointMake(margin, margin)
+ expectedOrigin:CGPointMake(CGRectGetMaxX(self.mapView.bounds) - margin - CGRectGetWidth(view.frame), 4)],
+ [MGLOrnamentTestData createWithPostion:MGLOrnamentPositionBottomLeft
+ offset:CGPointMake(margin, margin)
+ expectedOrigin:CGPointMake(margin, CGRectGetMaxY(self.mapView.bounds) - margin - bottomSafeAreaInset - CGRectGetHeight(view.frame))],
+ [MGLOrnamentTestData createWithPostion:MGLOrnamentPositionBottomRight
+ offset:CGPointMake(margin, margin)
+ expectedOrigin:CGPointMake(CGRectGetMaxX(self.mapView.bounds) - margin - CGRectGetWidth(view.frame),
+ CGRectGetMaxY(self.mapView.bounds) - margin - bottomSafeAreaInset - CGRectGetHeight(view.frame))]
+ ];
+}
+
+- (void)testCompassPlacement {
+ double accuracy = 0.01;
+ CGFloat margin = 4.0;
+
+ UIView *compassView = self.mapView.compassView;
+ NSArray *testDataList = [self makeTestDataListWithView:compassView margin:margin];
+
+ for (MGLOrnamentTestData *testData in testDataList) {
+ self.mapView.compassViewPosition = testData.position;
+ self.mapView.compassViewMargins = testData.margins;
+
+ //invoke layout
+ [self.superView setNeedsLayout];
+ [self.superView layoutIfNeeded];
+
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(compassView.frame), testData.expectedOrigin.x, accuracy);
+ XCTAssertEqualWithAccuracy(CGRectGetMinY(compassView.frame), testData.expectedOrigin.y, accuracy);
+ }
+}
+
+- (void)testScalebarPlacement {
+ CGFloat bottomSafeAreaInset = 0.0;
+ double accuracy = 0.01;
+ CGFloat margin = 4.0;
+
+ if (@available(iOS 11.0, *)) {
+ bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom;
+ }
+
+ UIView *scaleBar = self.mapView.scaleBar;
+ NSArray *testDataList = [self makeTestDataListWithView:scaleBar margin:margin];
+
+ for (MGLOrnamentTestData *testData in testDataList) {
+ self.mapView.scaleBarPosition = testData.position;
+ self.mapView.scaleBarMargins = testData.margins;
+
+ //invoke layout
+ [self.superView setNeedsLayout];
+ [self.superView layoutIfNeeded];
+
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(scaleBar.frame), testData.expectedOrigin.x, accuracy);
+ XCTAssertEqualWithAccuracy(CGRectGetMinY(scaleBar.frame), testData.expectedOrigin.y, accuracy);
+ }
+}
+
+- (void)testAttributionButtonPlacement {
+ CGFloat bottomSafeAreaInset = 0.0;
+ double accuracy = 0.01;
+ CGFloat margin = 4.0;
+
+ if (@available(iOS 11.0, *)) {
+ bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom;
+ }
+
+ UIView *attributionButton = self.mapView.attributionButton;
+ NSArray *testDataList = [self makeTestDataListWithView:attributionButton margin:margin];
+
+ for (MGLOrnamentTestData *testData in testDataList) {
+ self.mapView.attributionButtonPosition = testData.position;
+ self.mapView.attributionButtonMargins = testData.margins;
+
+ //invoke layout
+ [self.superView setNeedsLayout];
+ [self.superView layoutIfNeeded];
+
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(attributionButton.frame), testData.expectedOrigin.x, accuracy);
+ XCTAssertEqualWithAccuracy(CGRectGetMinY(attributionButton.frame), testData.expectedOrigin.y, accuracy);
+ }
+}
+
+- (void)testLogoPlacement {
+ CGFloat bottomSafeAreaInset = 0.0;
+ double accuracy = 0.01;
+ CGFloat margin = 4.0;
+
+ if (@available(iOS 11.0, *)) {
+ bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom;
+ }
+
+ UIView *logoView = self.mapView.logoView;
+ NSArray *testDataList = [self makeTestDataListWithView:logoView margin:margin];
+
+ for (MGLOrnamentTestData *testData in testDataList) {
+ self.mapView.logoViewPosition = testData.position;
+ self.mapView.logoViewMargins = testData.margins;
+
+ //invoke layout
+ [self.superView setNeedsLayout];
+ [self.superView layoutIfNeeded];
+
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(logoView.frame), testData.expectedOrigin.x, accuracy);
+ XCTAssertEqualWithAccuracy(CGRectGetMinY(logoView.frame), testData.expectedOrigin.y, accuracy);
+ }
+}
+
@end
diff --git a/platform/ios/test/MGLMapViewScaleBarTests.m b/platform/ios/test/MGLMapViewScaleBarTests.m
index 11d1187263..50864beb4d 100644
--- a/platform/ios/test/MGLMapViewScaleBarTests.m
+++ b/platform/ios/test/MGLMapViewScaleBarTests.m
@@ -35,7 +35,7 @@
XCTAssertFalse(scaleBar.hidden);
// Scale bar should not be visible at default zoom (~z0), but it should be ready.
- XCTAssertFalse(CGRectIsEmpty(scaleBar.frame));
+ XCTAssertFalse(CGSizeEqualToSize(scaleBar.intrinsicContentSize, CGSizeZero));
XCTAssertEqual(scaleBar.alpha, 0);
self.mapView.zoomLevel = 15;
@@ -49,7 +49,7 @@
XCTAssertFalse(scaleBar.hidden);
// Directly setting `.hidden` after the map has finished initializing will not update the scale bar.
- XCTAssertTrue(CGRectIsEmpty(scaleBar.frame));
+ XCTAssertTrue(CGSizeEqualToSize(scaleBar.intrinsicContentSize, CGSizeZero));
// ... but triggering any camera event will update it.
self.mapView.zoomLevel = 1;