summaryrefslogtreecommitdiff
path: root/platform/ios/src/MGLMapView.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src/MGLMapView.mm')
-rw-r--r--platform/ios/src/MGLMapView.mm469
1 files changed, 279 insertions, 190 deletions
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];
}
}