diff options
Diffstat (limited to 'platform/ios/src')
-rw-r--r-- | platform/ios/src/MGLMapView.h | 59 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 347 | ||||
-rw-r--r-- | platform/ios/src/MGLScaleBar.mm | 29 | ||||
-rw-r--r-- | platform/ios/src/UIView+MGLAdditions.h | 19 | ||||
-rw-r--r-- | platform/ios/src/UIView+MGLAdditions.m | 69 |
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 |