summaryrefslogtreecommitdiff
path: root/platform/ios/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src')
-rw-r--r--platform/ios/src/MGLMapView.h59
-rw-r--r--platform/ios/src/MGLMapView.mm347
-rw-r--r--platform/ios/src/MGLScaleBar.mm29
-rw-r--r--platform/ios/src/UIView+MGLAdditions.h19
-rw-r--r--platform/ios/src/UIView+MGLAdditions.m69
5 files changed, 298 insertions, 225 deletions
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index eae48adf0a..1ef64a587b 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`.
@@ -287,12 +305,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
@@ -304,6 +342,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.
@@ -329,6 +378,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 59bb1c6666..f19f1c8661 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -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;
-
- // 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];
-
- // 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];
-
- // 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];
-
- // 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)setScaleBarMargins:(CGPoint)scaleBarMargins {
+ MGLLogDebug(@"Setting scaleBarMargins: (x:%f, y:%f)", scaleBarMargins.x, scaleBarMargins.y);
+ _scaleBarMargins = scaleBarMargins;
+ [self installScaleBarConstraints];
}
-- (void)updateConstraints
-{
- // If safeAreaLayoutGuide API exists
- if (@available(iOS 11.0, *)) {
- UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
+- (void)setCompassViewPosition:(MGLOrnamentPosition)compassViewPosition {
+ MGLLogDebug(@"Setting compassViewPosition: %lu", compassViewPosition);
+ _compassViewPosition = compassViewPosition;
+ [self installCompassViewConstraints];
+}
- // 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];
- }
+- (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];
- [super updateConstraints];
+ 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];
}
-- (NSLayoutConstraint *)constraintForYAxisAnchor:(NSLayoutYAxisAnchor *)yAxisAnchor belowAnchor:(NSLayoutYAxisAnchor *)anchor
+- (void)installConstraints
{
- if (@available(iOS 11.0, *)) {
- return [yAxisAnchor constraintEqualToSystemSpacingBelowAnchor:anchor multiplier:1];
- } else {
- return nil;
- }
+ [self installCompassViewConstraints];
+ [self installScaleBarConstraints];
+ [self installLogoViewConstraints];
+ [self installAttributionButtonConstraints];
+}
+
+- (void)installCompassViewConstraints {
+ // compass view
+ [self updateConstraintsForOrnament:self.compassView
+ constraints:self.compassViewConstraints
+ position:self.compassViewPosition
+ size:self.compassView.bounds.size
+ margins:self.compassViewMargins];
+}
+
+- (void)installScaleBarConstraints {
+ // scale bar view
+ [self updateConstraintsForOrnament:self.scaleBar
+ constraints:self.scaleBarConstraints
+ position:self.scaleBarPosition
+ size:self.scaleBar.intrinsicContentSize
+ margins:self.scaleBarMargins];
+}
+
+- (void)installLogoViewConstraints {
+ // logo view
+ [self updateConstraintsForOrnament:self.logoView
+ constraints:self.logoViewConstraints
+ position:self.logoViewPosition
+ size:self.logoView.bounds.size
+ margins:self.logoViewMargins];
+}
+
+- (void)installAttributionButtonConstraints {
+ // attribution button
+ [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];
+ }
}
}
diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm
index f17d7b7ad2..9590a99438 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 {
@@ -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;
diff --git a/platform/ios/src/UIView+MGLAdditions.h b/platform/ios/src/UIView+MGLAdditions.h
new file mode 100644
index 0000000000..85fea31ad2
--- /dev/null
+++ b/platform/ios/src/UIView+MGLAdditions.h
@@ -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
diff --git a/platform/ios/src/UIView+MGLAdditions.m b/platform/ios/src/UIView+MGLAdditions.m
new file mode 100644
index 0000000000..43c54409bd
--- /dev/null
+++ b/platform/ios/src/UIView+MGLAdditions.m
@@ -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