[iOS] Enable developers to change position of ornaments (#13911)

* APIs for change position of ornaments

* Use anchors APIs and emove iOS8 layout code

* Add ornaments layout tests
This commit is contained in:
Lloyd Sheng
2019-03-11 23:06:16 +08:00
committed by GitHub
parent 5ccc5b7c36
commit 60ceac5efc
13 changed files with 560 additions and 231 deletions

View File

@@ -8,6 +8,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Added an MGLMapView.prefetchesTiles property that you can disable if you don’t want to prefetch simplified tiles as a performance optimization. ([#14031](https://github.com/mapbox/mapbox-gl-native/pull/14031))
* Fixed an issue that caused `MGL_FUNCTION` to ignore multiple formatting parameters when passed a `format` function as parameter. ([#14064](https://github.com/mapbox/mapbox-gl-native/pull/14064))
### User interaction
* Added `MGLOrnamentPosition` enum and margins methods to customize MGLMapView's scale bar, compass, logo and attribution position. ([#13911](https://github.com/mapbox/mapbox-gl-native/pull/13911))
## 4.9.0 - February 27, 2019
* Fixed a bug where setting `MGLMapView.userTrackingMode` to `MGLUserTrackingModeFollowWithHeading` or `MGLUserTrackingModeFollowWithCourse` would be ignored if the user’s location was not already available. ([#13849](https://github.com/mapbox/mapbox-gl-native/pull/13849))

View File

@@ -0,0 +1,5 @@
#import <UIKit/UIKit.h>
@interface MBXOrnamentsViewController : UIViewController
@end

View File

@@ -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.bounds];
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

View File

@@ -7,6 +7,7 @@
#import "MBXUserLocationAnnotationView.h"
#import "LimeGreenStyleLayer.h"
#import "MBXEmbeddedMapViewController.h"
#import "MBXOrnamentsViewController.h"
#import "MBXFrameTimeGraphView.h"
@@ -104,8 +105,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousShouldLimitCameraChanges,
MBXSettingsMiscellaneousShowCustomLocationManager,
MBXSettingsMiscellaneousOrnamentsPlacement,
MBXSettingsMiscellaneousPrintLogFile,
MBXSettingsMiscellaneousDeleteLogFile,
MBXSettingsMiscellaneousDeleteLogFile
};
// Utility methods
@@ -499,6 +501,7 @@ CLLocationCoordinate2D randomWorldCoordinate() {
@"Show Snapshots",
[NSString stringWithFormat:@"%@ Camera Changes", (_shouldLimitCameraChanges ? @"Unlimit" : @"Limit")],
@"View Route Simulation",
@"Ornaments Placement",
]];
if (self.debugLoggingEnabled)
@@ -756,6 +759,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;

View File

@@ -347,6 +347,11 @@
55E5666D21C2A2080008B8B5 /* NSData+MMEGZIP.h in Headers */ = {isa = PBXBuildFile; fileRef = 40834BCF1FE05D7100C1BD0D /* NSData+MMEGZIP.h */; };
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */; };
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; };
6F018BAE220031B8003E7269 /* UIView+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FAFA29B220023840088709E /* UIView+MGLAdditions.m */; };
6F018BAF220031BF003E7269 /* UIView+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FAFA29A220023840088709E /* UIView+MGLAdditions.h */; };
6F018BB0220031BF003E7269 /* UIView+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FAFA29A220023840088709E /* UIView+MGLAdditions.h */; };
6F018BB1220031C1003E7269 /* UIView+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FAFA29B220023840088709E /* UIView+MGLAdditions.m */; };
6FA9341721EF372100AA9CA8 /* MBXOrnamentsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA9341521EF372100AA9CA8 /* MBXOrnamentsViewController.m */; };
74CB5EB1219B252C00102936 /* MGLStyleLayerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CB5EAF219B252C00102936 /* MGLStyleLayerManager.h */; };
74CB5EB2219B252C00102936 /* MGLStyleLayerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CB5EAF219B252C00102936 /* MGLStyleLayerManager.h */; };
74CB5EB3219B252C00102936 /* MGLStyleLayerManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CB5EB0219B252C00102936 /* MGLStyleLayerManager.mm */; };
@@ -1076,6 +1081,10 @@
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>"; };
6FAFA29A220023840088709E /* UIView+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+MGLAdditions.h"; sourceTree = "<group>"; };
6FAFA29B220023840088709E /* UIView+MGLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+MGLAdditions.m"; sourceTree = "<group>"; };
74CB5EAF219B252C00102936 /* MGLStyleLayerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayerManager.h; sourceTree = "<group>"; };
74CB5EB0219B252C00102936 /* MGLStyleLayerManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyleLayerManager.mm; sourceTree = "<group>"; };
74CB5EB5219B280300102936 /* MGLHillshadeStyleLayer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHillshadeStyleLayer_Private.h; sourceTree = "<group>"; };
@@ -1677,6 +1686,8 @@
30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */,
DD9BE4F51EB263C50079A3AF /* UIViewController+MGLAdditions.h */,
DD9BE4F61EB263C50079A3AF /* UIViewController+MGLAdditions.m */,
6FAFA29A220023840088709E /* UIView+MGLAdditions.h */,
6FAFA29B220023840088709E /* UIView+MGLAdditions.m */,
);
name = Categories;
sourceTree = "<group>";
@@ -1950,6 +1961,8 @@
965DF51020F9430500438AAC /* MBXFrameTimeGraphView.m */,
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */,
632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */,
6FA9341621EF372100AA9CA8 /* MBXOrnamentsViewController.h */,
6FA9341521EF372100AA9CA8 /* MBXOrnamentsViewController.m */,
DA821D051CCC6D59007508D4 /* Main.storyboard */,
DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */,
DA1DC99E1CB6E088006E619F /* Assets.xcassets */,
@@ -2421,6 +2434,7 @@
35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */,
FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
6F018BB0220031BF003E7269 /* UIView+MGLAdditions.h in Headers */,
1FCAE2A820B88B3800C577DD /* MGLLocationManager_Private.h in Headers */,
DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */,
DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
@@ -2629,6 +2643,7 @@
3510FFEB1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */,
35E1A4D91D74336F007AA97F /* MGLValueEvaluator.h in Headers */,
DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */,
6F018BAF220031BF003E7269 /* UIView+MGLAdditions.h in Headers */,
96E516EA2000560B00A02306 /* MGLAnnotationView_Private.h in Headers */,
96E516FB20005A4000A02306 /* MGLUserLocationHeadingBeamLayer.h in Headers */,
96E516DC2000547000A02306 /* MGLPolyline_Private.h in Headers */,
@@ -3083,6 +3098,7 @@
354B839C1D2E9B48005D9406 /* MBXUserLocationAnnotationView.m in Sources */,
965DF51120F9430500438AAC /* MBXFrameTimeGraphView.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 */,
@@ -3182,6 +3198,7 @@
40834C4A1FE05F7500C1BD0D /* TSKPinningValidator.m in Sources */,
967C864D210A9D3C004DF794 /* UIDevice+MGLAdditions.m in Sources */,
400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */,
6F018BAE220031B8003E7269 /* UIView+MGLAdditions.m in Sources */,
96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
ACA65F592140697200537748 /* MMEDispatchManager.m in Sources */,
40834BF31FE05E1800C1BD0D /* MMETimerManager.m in Sources */,
@@ -3318,6 +3335,7 @@
35136D431D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
967C864E210A9D3C004DF794 /* UIDevice+MGLAdditions.m in Sources */,
96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
6F018BB1220031C1003E7269 /* UIView+MGLAdditions.m in Sources */,
40834C071FE05E1800C1BD0D /* MMETimerManager.m in Sources */,
ACA65F5A2140697200537748 /* MMEDispatchManager.m in Sources */,
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,

View File

@@ -30,6 +30,7 @@
"platform/ios/vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator.m",
"platform/ios/src/UIDevice+MGLAdditions.m",
"platform/darwin/src/NSArray+MGLAdditions.mm",
"platform/ios/src/UIView+MGLAdditions.m",
"platform/ios/src/NSOrthography+MGLAdditions.m",
"platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEDispatchManager.m",
"platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.m",
@@ -234,6 +235,7 @@
"MGLShape_Private.h": "platform/darwin/src/MGLShape_Private.h",
"MGLPolygon_Private.h": "platform/darwin/src/MGLPolygon_Private.h",
"MGLStyleLayer_Private.h": "platform/darwin/src/MGLStyleLayer_Private.h",
"UIView+MGLAdditions.h": "platform/ios/src/UIView+MGLAdditions.h",
"MGLLocationManager_Private.h": "platform/darwin/src/MGLLocationManager_Private.h",
"MGLLoggingConfiguration_Private.h": "platform/darwin/src/MGLLoggingConfiguration_Private.h",
"NSComparisonPredicate+MGLAdditions.h": "platform/darwin/src/NSComparisonPredicate+MGLAdditions.h",

View File

@@ -48,6 +48,24 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
MGLAnnotationVerticalAlignmentBottom,
};
/**
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`.
@@ -286,12 +304,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.
@@ -303,6 +341,17 @@ 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.
@@ -328,6 +377,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.

View File

@@ -55,6 +55,7 @@
#import "UIDevice+MGLAdditions.h"
#import "UIImage+MGLAdditions.h"
#import "UIViewController+MGLAdditions.h"
#import "UIView+MGLAdditions.h"
#import "MGLFaux3DUserLocationAnnotationView.h"
#import "MGLUserLocationAnnotationView.h"
@@ -97,6 +98,8 @@ const MGLExceptionName MGLUserLocationAnnotationTypeException = @"MGLUserLocatio
const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundException";
const MGLExceptionName MGLUnderlyingMapUnavailableException = @"MGLUnderlyingMapUnavailableException";
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.
@@ -521,6 +524,8 @@ public:
_logoView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_logoView];
_logoViewConstraints = [NSMutableArray array];
_logoViewPosition = MGLOrnamentPositionBottomLeft;
_logoViewMargins = MGLOrnamentDefaultPositionOffset;
// setup attribution
//
@@ -535,6 +540,8 @@ public:
UILongPressGestureRecognizer *attributionLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showAttribution:)];
[_attributionButton addGestureRecognizer:attributionLongPress];
_attributionButtonPosition = MGLOrnamentPositionBottomRight;
_attributionButtonMargins = MGLOrnamentDefaultPositionOffset;
// setup compass
//
@@ -548,6 +555,8 @@ public:
_compassView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_compassView];
_compassViewConstraints = [NSMutableArray array];
_compassViewPosition = MGLOrnamentPositionTopRight;
_compassViewMargins = MGLOrnamentDefaultPositionOffset;
// setup scale control
//
@@ -555,7 +564,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:)];
@@ -811,222 +824,131 @@ public:
return YES;
}
- (UIViewController *)viewControllerForLayoutGuides
{
// Per -[UIResponder nextResponder] documentation, a UIView’s next responder
// is its managing UIViewController if applicable, or otherwise its
// superview. UIWindow’s next responder is UIApplication, which has no next
// responder.
UIResponder *laterResponder = self;
while ([laterResponder isKindOfClass:[UIView class]])
{
laterResponder = laterResponder.nextResponder;
}
if ([laterResponder isKindOfClass:[UIViewController class]])
{
return (UIViewController *)laterResponder;
}
return nil;
- (void)setScaleBarPosition:(MGLOrnamentPosition)scaleBarPosition {
MGLLogDebug(@"Setting scaleBarPosition: %lu", scaleBarPosition);
_scaleBarPosition = scaleBarPosition;
[self installScaleBarConstraints];
}
- (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;
- (void)setScaleBarMargins:(CGPoint)scaleBarMargins {
MGLLogDebug(@"Setting scaleBarMargins: (x:%f, y:%f)", scaleBarMargins.x, scaleBarMargins.y);
_scaleBarMargins = scaleBarMargins;
[self installScaleBarConstraints];
}
- (void)setCompassViewPosition:(MGLOrnamentPosition)compassViewPosition {
MGLLogDebug(@"Setting compassViewPosition: %lu", compassViewPosition);
_compassViewPosition = compassViewPosition;
[self installCompassViewConstraints];
}
- (void)setCompassViewMargins:(CGPoint)compassViewMargins {
MGLLogDebug(@"Setting compassViewOffset: (x:%f, y:%f)", compassViewMargins.x, compassViewMargins.y);
_compassViewMargins = compassViewMargins;
[self installCompassViewConstraints];
}
- (void)setLogoViewPosition:(MGLOrnamentPosition)logoViewPosition {
MGLLogDebug(@"Setting logoViewPosition: %lu", logoViewPosition);
_logoViewPosition = logoViewPosition;
[self installLogoViewConstraints];
}
- (void)setLogoViewMargins:(CGPoint)logoViewMargins {
MGLLogDebug(@"Setting logoViewMargins: (x:%f, y:%f)", logoViewMargins.x, logoViewMargins.y);
_logoViewMargins = logoViewMargins;
[self installLogoViewConstraints];
}
- (void)setAttributionButtonPosition:(MGLOrnamentPosition)attributionButtonPosition {
MGLLogDebug(@"Setting attributionButtonPosition: %lu", attributionButtonPosition);
_attributionButtonPosition = attributionButtonPosition;
[self installAttributionButtonConstraints];
}
- (void)setAttributionButtonMargins:(CGPoint)attributionButtonMargins {
MGLLogDebug(@"Setting attributionButtonMargins: (x:%f, y:%f)", attributionButtonMargins.x, attributionButtonMargins.y);
_attributionButtonMargins = attributionButtonMargins;
[self installAttributionButtonConstraints];
}
- (void)updateConstraintsForOrnament:(UIView *)view
constraints:(NSMutableArray *)constraints
position:(MGLOrnamentPosition)position
size:(CGSize)size
margins:(CGPoint)margins {
NSMutableArray *updatedConstraints = [NSMutableArray array];
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]];
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]];
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]];
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]];
break;
}
[updatedConstraints addObject:[view.widthAnchor constraintEqualToConstant:size.width]];
[updatedConstraints addObject:[view.heightAnchor constraintEqualToConstant:size.height]];
[NSLayoutConstraint deactivateConstraints:constraints];
[constraints removeAllObjects];
[NSLayoutConstraint activateConstraints:updatedConstraints];
[constraints addObjectsFromArray:updatedConstraints];
}
- (void)installConstraints
{
[self installCompassViewConstraints];
[self installScaleBarConstraints];
[self installLogoViewConstraints];
[self installAttributionButtonConstraints];
}
- (void)installCompassViewConstraints {
// 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]];
[containerView addConstraints:self.compassViewConstraints];
[self updateConstraintsForOrnament:self.compassView
constraints:self.compassViewConstraints
position:self.compassViewPosition
size:self.compassView.bounds.size
margins:self.compassViewMargins];
}
- (void)installScaleBarConstraints {
// 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
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];
[self updateConstraintsForOrnament:self.scaleBar
constraints:self.scaleBarConstraints
position:self.scaleBarPosition
size:self.scaleBar.intrinsicContentSize
margins:self.scaleBarMargins];
}
- (void)installLogoViewConstraints {
// 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
multiplier:1.0
constant:8.0 + self.contentInset.bottom]];
}
[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];
[self updateConstraintsForOrnament:self.logoView
constraints:self.logoViewConstraints
position:self.logoViewPosition
size:self.logoView.bounds.size
margins:self.logoViewMargins];
}
- (void)installAttributionButtonConstraints {
// 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];
}
- (void)updateConstraints
{
// If safeAreaLayoutGuide API exists
if (@available(iOS 11.0, *)) {
UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
// 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];
}
- (NSLayoutConstraint *)constraintForYAxisAnchor:(NSLayoutYAxisAnchor *)yAxisAnchor belowAnchor:(NSLayoutYAxisAnchor *)anchor
{
if (@available(iOS 11.0, *)) {
return [yAxisAnchor constraintEqualToSystemSpacingBelowAnchor:anchor multiplier:1];
} else {
return nil;
}
[self updateConstraintsForOrnament:self.attributionButton
constraints:self.attributionButtonConstraints
position:self.attributionButtonPosition
size:self.attributionButton.bounds.size
margins:self.attributionButtonMargins];
}
- (BOOL)isOpaque
@@ -1151,7 +1073,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.
@@ -1317,6 +1239,7 @@ public:
- (void)didMoveToSuperview
{
[self validateDisplayLink];
[self installConstraints];
[super didMoveToSuperview];
}
@@ -2709,7 +2632,7 @@ public:
- (CGRect)accessibilityFrame
{
CGRect frame = [super accessibilityFrame];
UIViewController *viewController = self.viewControllerForLayoutGuides;
UIViewController *viewController = self.mgl_viewControllerForLayoutGuides;
if (viewController)
{
CGFloat topInset = viewController.topLayoutGuide.length;
@@ -6516,7 +6439,11 @@ public:
// setting this property.
if ( ! self.scaleBar.hidden)
{
CGSize originalSize = self.scaleBar.intrinsicContentSize;
[(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]];
if ( ! CGSizeEqualToSize(originalSize, self.scaleBar.intrinsicContentSize)) {
[self installScaleBarConstraints];
}
}
}

View File

@@ -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 {
@@ -256,12 +256,9 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
[self updateVisibility];
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 {
@@ -371,7 +368,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;
}
@@ -398,7 +397,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
- (void)layoutBars {
CGFloat barWidth = round((CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count);
CGFloat barWidth = round((self.intrinsicContentSize.width - self.borderWidth * 2.0f) / self.bars.count);
NSUInteger i = 0;
for (UIView *bar in self.bars) {
@@ -409,7 +408,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);
@@ -421,12 +420,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;

View File

@@ -0,0 +1,19 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (MGLAdditions)
- (UIViewController *)mgl_viewControllerForLayoutGuides;
- (NSLayoutYAxisAnchor *)mgl_safeTopAnchor;
- (NSLayoutXAxisAnchor *)mgl_safeLeadingAnchor;
- (NSLayoutYAxisAnchor *)mgl_safeBottomAnchor;
- (NSLayoutXAxisAnchor *)mgl_safeTrailingAnchor;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,69 @@
#import "UIView+MGLAdditions.h"
@implementation UIView (MGLAdditions)
- (UIViewController *)mgl_viewControllerForLayoutGuides
{
// Per -[UIResponder nextResponder] documentation, a UIView’s next responder
// is its managing UIViewController if applicable, or otherwise its
// superview. UIWindow’s next responder is UIApplication, which has no next
// responder.
UIResponder *laterResponder = self;
while ([laterResponder isKindOfClass:[UIView class]])
{
laterResponder = laterResponder.nextResponder;
}
if ([laterResponder isKindOfClass:[UIViewController class]])
{
return (UIViewController *)laterResponder;
}
return nil;
}
- (NSLayoutYAxisAnchor *)mgl_safeTopAnchor {
if (@available(iOS 11.0, *)) {
return self.safeAreaLayoutGuide.topAnchor;
} else {
UIViewController *viewController = self.mgl_viewControllerForLayoutGuides;
BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
if (useLayoutGuides) {
return viewController.topLayoutGuide.bottomAnchor;
}
else {
return self.topAnchor;
}
}
}
- (NSLayoutXAxisAnchor *)mgl_safeLeadingAnchor {
if (@available(iOS 11.0, *)) {
return self.safeAreaLayoutGuide.leadingAnchor;
} else {
return self.leadingAnchor;
}
}
- (NSLayoutYAxisAnchor *)mgl_safeBottomAnchor {
if (@available(iOS 11.0, *)) {
return self.safeAreaLayoutGuide.bottomAnchor;
} else {
UIViewController *viewController = self.mgl_viewControllerForLayoutGuides;
BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
if (useLayoutGuides) {
return viewController.bottomLayoutGuide.topAnchor;
}
else {
return self.bottomAnchor;
}
}
}
- (NSLayoutXAxisAnchor *)mgl_safeTrailingAnchor {
if (@available(iOS 11.0, *)) {
return self.safeAreaLayoutGuide.trailingAnchor;
} else {
return self.trailingAnchor;
}
}
@end

View File

@@ -4,6 +4,27 @@
#import "MGLAccountManager.h"
@interface MGLOrnamentTestData : NSObject
@property (nonatomic) MGLOrnamentPosition position;
@property (nonatomic) CGPoint offset;
@property (nonatomic) CGPoint expectedOrigin;
@end
@implementation MGLOrnamentTestData
+ (instancetype)createWithPosition:(MGLOrnamentPosition)position offset:(CGPoint)offset expectedOrigin:(CGPoint)expectedOrigin {
MGLOrnamentTestData *data = [[MGLOrnamentTestData alloc] init];
data.position = position;
data.offset = offset;
data.expectedOrigin = expectedOrigin;
return data;
}
@end
@interface MGLMapViewLayoutTests : XCTestCase<MGLMapViewDelegate>
@property (nonatomic) UIView *superView;
@@ -107,4 +128,107 @@
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 createWithPosition:MGLOrnamentPositionTopLeft
offset:CGPointMake(margin, margin)
expectedOrigin:CGPointMake(margin, margin)],
[MGLOrnamentTestData createWithPosition:MGLOrnamentPositionTopRight
offset:CGPointMake(margin, margin)
expectedOrigin:CGPointMake(CGRectGetMaxX(self.mapView.bounds) - margin - CGRectGetWidth(view.frame), 4)],
[MGLOrnamentTestData createWithPosition:MGLOrnamentPositionBottomLeft
offset:CGPointMake(margin, margin)
expectedOrigin:CGPointMake(margin, CGRectGetMaxY(self.mapView.bounds) - margin - bottomSafeAreaInset - CGRectGetHeight(view.frame))],
[MGLOrnamentTestData createWithPosition: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.offset;
//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 {
double accuracy = 0.01;
CGFloat margin = 4.0;
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.offset;
//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 {
double accuracy = 0.01;
CGFloat margin = 4.0;
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.offset;
//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 {
double accuracy = 0.01;
CGFloat margin = 4.0;
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.offset;
//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

View File

@@ -36,7 +36,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;
@@ -50,11 +50,11 @@
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;
XCTAssertFalse(CGRectIsEmpty(scaleBar.frame));
XCTAssertFalse(CGSizeEqualToSize(scaleBar.intrinsicContentSize, CGSizeZero));
XCTAssertEqual(scaleBar.alpha, 0);
self.mapView.zoomLevel = 15;