diff options
Diffstat (limited to 'platform/ios/src')
-rw-r--r-- | platform/ios/src/MGLCompassButton.h | 22 | ||||
-rw-r--r-- | platform/ios/src/MGLCompassButton.mm | 128 | ||||
-rw-r--r-- | platform/ios/src/MGLCompassButton_Private.h | 19 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.h | 9 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 105 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView_Private.h | 3 | ||||
-rw-r--r-- | platform/ios/src/Mapbox.h | 1 | ||||
-rw-r--r-- | platform/ios/src/UIImage+MGLAdditions.h | 6 | ||||
-rw-r--r-- | platform/ios/src/UIImage+MGLAdditions.mm | 18 |
9 files changed, 211 insertions, 100 deletions
diff --git a/platform/ios/src/MGLCompassButton.h b/platform/ios/src/MGLCompassButton.h new file mode 100644 index 0000000000..9c3d9d1c77 --- /dev/null +++ b/platform/ios/src/MGLCompassButton.h @@ -0,0 +1,22 @@ +#import <UIKit/UIKit.h> + +#import "MGLTypes.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + A specialized view that displays the current compass heading for its associated map. + */ +MGL_EXPORT +@interface MGLCompassButton : UIImageView + +/** + The visibility of the compass button. + + You can configure a compass button to be visible all the time or only when the compass heading changes. + */ +@property (nonatomic, assign) MGLOrnamentVisibility compassVisibility; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MGLCompassButton.mm b/platform/ios/src/MGLCompassButton.mm new file mode 100644 index 0000000000..94a6820a74 --- /dev/null +++ b/platform/ios/src/MGLCompassButton.mm @@ -0,0 +1,128 @@ +#import "MGLCompassButton_Private.h" +#import "MGLCompassDirectionFormatter.h" + +#import <Mapbox/MGLGeometry.h> + +#import "MGLMapView_Private.h" +#import "UIImage+MGLAdditions.h" +#import "NSBundle+MGLAdditions.h" + +#include <mbgl/math/wrap.hpp> + +@interface MGLCompassButton () + +@property (nonatomic, weak) MGLMapView *mapView; +@property (nonatomic) MGLCompassDirectionFormatter *accessibilityCompassFormatter; + +@end + +@implementation MGLCompassButton + ++ (instancetype)compassButtonWithMapView:(MGLMapView *)mapView { + return [[MGLCompassButton alloc] initWithMapView:mapView]; +} + +- (instancetype)initWithMapView:(MGLMapView *)mapView { + if (self = [super init]) { + self.mapView = mapView; + [self commonInit]; + } + return self; +} + +- (void)commonInit { + self.image = self.compassImage; + + self.compassVisibility = MGLOrnamentVisibilityAdaptive; + + self.alpha = 0; + self.userInteractionEnabled = YES; + self.translatesAutoresizingMaskIntoConstraints = NO; + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; + [self addGestureRecognizer:tapGesture]; + + self.accessibilityTraits = UIAccessibilityTraitButton; + self.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label"); + self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint"); + + self.accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init]; + self.accessibilityCompassFormatter.unitStyle = NSFormattingUnitStyleLong; + + [self sizeToFit]; +} + +- (void)setCompassVisibility:(MGLOrnamentVisibility)compassVisibility { + if (_compassVisibility == compassVisibility) { return; } + _compassVisibility = compassVisibility; + + [self updateCompassAnimated:NO]; +} + +- (UIImage *)compassImage { + UIImage *scaleImage = [UIImage mgl_resourceImageNamed:@"Compass"]; + UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, UIScreen.mainScreen.scale); + [scaleImage drawInRect:{CGPointZero, scaleImage.size}]; + + NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{ + NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight], + NSForegroundColorAttributeName: [UIColor whiteColor], + }]; + CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2, + scaleImage.size.height * 0.435, + north.size.width, north.size.height); + [north drawInRect:stringRect]; + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + +- (void)handleTapGesture:(__unused UITapGestureRecognizer *)sender { + [self.mapView resetNorth]; +} + +- (void)updateCompass { + [self updateCompassAnimated:YES]; +} + +- (void)updateCompassAnimated:(BOOL)animated { + CLLocationDirection direction = self.mapView.direction; + CLLocationDirection plateDirection = mbgl::util::wrap(-direction, 0., 360.); + self.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(plateDirection)); + + self.isAccessibilityElement = direction > 0; + self.accessibilityValue = [self.accessibilityCompassFormatter stringFromDirection:direction]; + + switch (self.compassVisibility) { + case MGLOrnamentVisibilityAdaptive: + if (direction > 0 && self.alpha < 1) { + [self showCompass:animated]; + } else if (direction == 0 && self.alpha > 0) { + [self hideCompass:animated]; + } + break; + case MGLOrnamentVisibilityVisible: + [self showCompass:animated]; + break; + case MGLOrnamentVisibilityHidden: + [self hideCompass:animated]; + break; + } +} + +- (void)showCompass:(BOOL)animated { + animated ? [self animateToAlpha:1] : [self setAlpha:1]; +} + +- (void)hideCompass:(BOOL)animated { + animated ? [self animateToAlpha:0] : [self setAlpha:0]; +} + +- (void)animateToAlpha:(CGFloat)alpha { + [UIView animateWithDuration:MGLAnimationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ + self.alpha = alpha; + } completion:nil]; +} + +@end diff --git a/platform/ios/src/MGLCompassButton_Private.h b/platform/ios/src/MGLCompassButton_Private.h new file mode 100644 index 0000000000..c9741d79e3 --- /dev/null +++ b/platform/ios/src/MGLCompassButton_Private.h @@ -0,0 +1,19 @@ +#import <UIKit/UIKit.h> + +#import <Mapbox/MGLCompassButton.h> + +@class MGLMapView; + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLCompassButton (Private) + ++ (instancetype)compassButtonWithMapView:(MGLMapView *)mapView; + +@property (nonatomic, weak) MGLMapView *mapView; + +- (void)updateCompass; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 87c24cd97b..9fe9a6c10a 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -1,9 +1,9 @@ -#import "MGLGeometry.h" -#import "MGLMapCamera.h" - #import <UIKit/UIKit.h> +#import "MGLCompassButton.h" #import "MGLFoundation.h" +#import "MGLGeometry.h" +#import "MGLMapCamera.h" #import "MGLTypes.h" NS_ASSUME_NONNULL_BEGIN @@ -122,7 +122,6 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLMapViewPreferredFramesPerSecond MGLMapView FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLMissingLocationServicesUsageDescriptionException; FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUserLocationAnnotationTypeException; -FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLResourceNotFoundException; /** An interactive, customizable map view with an interface similar to the one @@ -316,7 +315,7 @@ MGL_EXPORT 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; +@property (nonatomic, readonly) MGLCompassButton *compassView; /** The position of the compass view. The default value is `MGLOrnamentPositionTopRight`. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index c0b7380607..d027f616bd 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -55,11 +55,11 @@ #import "MGLUserLocation_Private.h" #import "MGLAnnotationImage_Private.h" #import "MGLAnnotationView_Private.h" +#import "MGLCompassButton_Private.h" #import "MGLScaleBar.h" #import "MGLStyle_Private.h" #import "MGLStyleLayer_Private.h" #import "MGLMapboxEvents.h" -#import "MMEConstants.h" #import "MGLSDKUpdateChecker.h" #import "MGLCompactCalloutView.h" #import "MGLAnnotationContainerView.h" @@ -68,6 +68,7 @@ #import "MGLMapAccessibilityElement.h" #import "MGLLocationManager_Private.h" #import "MGLLoggingConfiguration_Private.h" +#import "MMEConstants.h" #include <algorithm> #include <cstdlib> @@ -86,7 +87,6 @@ const MGLMapViewPreferredFramesPerSecond MGLMapViewPreferredFramesPerSecondMaxim const MGLExceptionName MGLMissingLocationServicesUsageDescriptionException = @"MGLMissingLocationServicesUsageDescriptionException"; const MGLExceptionName MGLUserLocationAnnotationTypeException = @"MGLUserLocationAnnotationTypeException"; -const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundException"; const MGLExceptionName MGLUnderlyingMapUnavailableException = @"MGLUnderlyingMapUnavailableException"; const CGPoint MGLOrnamentDefaultPositionOffset = CGPointMake(8, 8); @@ -197,7 +197,7 @@ public: @property (nonatomic) NSMutableArray<NSLayoutConstraint *> *scaleBarConstraints; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; -@property (nonatomic, readwrite) UIImageView *compassView; +@property (nonatomic, readwrite) MGLCompassButton *compassView; @property (nonatomic) NSMutableArray<NSLayoutConstraint *> *compassViewConstraints; @property (nonatomic, readwrite) UIImageView *logoView; @property (nonatomic) NSMutableArray<NSLayoutConstraint *> *logoViewConstraints; @@ -308,7 +308,6 @@ public: BOOL _delegateHasFillColorsForShapeAnnotations; BOOL _delegateHasLineWidthsForShapeAnnotations; - MGLCompassDirectionFormatter *_accessibilityCompassFormatter; NSArray<id <MGLFeature>> *_visiblePlaceFeatures; NSArray<id <MGLFeature>> *_visibleRoadFeatures; NSMutableSet<MGLFeatureAccessibilityElement *> *_featureAccessibilityElements; @@ -436,8 +435,6 @@ public: // self.isAccessibilityElement = YES; self.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"MAP_A11Y_LABEL", nil, nil, @"Map", @"Accessibility label"); self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction | UIAccessibilityTraitAdjustable; - _accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init]; - _accessibilityCompassFormatter.unitStyle = NSFormattingUnitStyleLong; self.backgroundColor = [UIColor clearColor]; self.clipsToBounds = YES; if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = YES; } @@ -510,7 +507,7 @@ public: // setup logo // - UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox"]; + UIImage *logo = [UIImage mgl_resourceImageNamed:@"mapbox"]; _logoView = [[UIImageView alloc] initWithImage:logo]; _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); @@ -538,14 +535,7 @@ public: // setup compass // - _compassView = [[UIImageView alloc] initWithImage:self.compassImage]; - _compassView.alpha = 0; - _compassView.userInteractionEnabled = YES; - [_compassView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleCompassTapGesture:)]]; - _compassView.accessibilityTraits = UIAccessibilityTraitButton; - _compassView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label"); - _compassView.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint"); - _compassView.translatesAutoresizingMaskIntoConstraints = NO; + _compassView = [MGLCompassButton compassButtonWithMapView:self]; [self addSubview:_compassView]; _compassViewConstraints = [NSMutableArray array]; _compassViewPosition = MGLOrnamentPositionTopRight; @@ -664,26 +654,6 @@ public: static_cast<uint32_t>(size.height) }; } -- (UIImage *)compassImage -{ - UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass"]; - UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale); - [scaleImage drawInRect:{ CGPointZero, scaleImage.size }]; - - NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{ - NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight], - NSForegroundColorAttributeName: [UIColor whiteColor], - }]; - CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2, - scaleImage.size.height * 0.435, - north.size.width, north.size.height); - [north drawInRect:stringRect]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return image; -} - - (void)reachabilityChanged:(NSNotification *)notification { MGLAssertIsMainThread(); @@ -1522,19 +1492,6 @@ public: #pragma mark - Gestures - -- (void)handleCompassTapGesture:(__unused id)sender -{ - self.cameraChangeReasonBitmask |= MGLCameraChangeReasonResetNorth; - - [self resetNorthAnimated:YES]; - - if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading || - self.userTrackingMode == MGLUserTrackingModeFollowWithCourse) - { - self.userTrackingMode = MGLUserTrackingModeFollow; - } -} - - (void)touchesBegan:(__unused NSSet<UITouch *> *)touches withEvent:(__unused UIEvent *)event { if (!self.zoomEnabled && !self.pitchEnabled && !self.rotateEnabled && !self.scrollEnabled) @@ -2547,6 +2504,8 @@ public: - (void)resetNorthAnimated:(BOOL)animated { + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonResetNorth; + [self setDirection:0 animated:animated]; } @@ -3467,7 +3426,8 @@ public: MGLLogDebug(@"Setting direction: %f animated: %@", direction, MGLStringFromBOOL(animated)); if ( ! animated && ! self.rotationAllowed) return; - if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading) + if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading || + self.userTrackingMode == MGLUserTrackingModeFollowWithCourse) { self.userTrackingMode = MGLUserTrackingModeFollow; } @@ -4177,7 +4137,7 @@ public: /// rect therefore excludes the bottom half. - (MGLAnnotationImage *)defaultAnnotationImage { - UIImage *image = [MGLMapView resourceImageNamed:MGLDefaultStyleMarkerSymbolName]; + UIImage *image = [UIImage mgl_resourceImageNamed:MGLDefaultStyleMarkerSymbolName]; image = [image imageWithAlignmentRectInsets: UIEdgeInsetsMake(0, 0, image.size.height / 2, 0)]; MGLAnnotationImage *annotationImage = [MGLAnnotationImage annotationImageWithImage:image @@ -6547,35 +6507,7 @@ public: - (void)updateCompass { - CLLocationDirection direction = self.direction; - CLLocationDirection plateDirection = mbgl::util::wrap(-direction, 0., 360.); - self.compassView.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(plateDirection)); - - self.compassView.isAccessibilityElement = direction > 0; - self.compassView.accessibilityValue = [_accessibilityCompassFormatter stringFromDirection:direction]; - - if (direction > 0 && self.compassView.alpha < 1) - { - [UIView animateWithDuration:MGLAnimationDuration - delay:0 - options:UIViewAnimationOptionBeginFromCurrentState - animations:^ - { - self.compassView.alpha = 1; - } - completion:nil]; - } - else if (direction == 0 && self.compassView.alpha > 0) - { - [UIView animateWithDuration:MGLAnimationDuration - delay:0 - options:UIViewAnimationOptionBeginFromCurrentState - animations:^ - { - self.compassView.alpha = 0; - } - completion:nil]; - } + [self.compassView updateCompass]; } - (void)updateScaleBar @@ -6593,21 +6525,6 @@ public: } } -+ (UIImage *)resourceImageNamed:(NSString *)imageName -{ - UIImage *image = [UIImage imageNamed:imageName - inBundle:[NSBundle mgl_frameworkBundle] - compatibleWithTraitCollection:nil]; - - if ( ! image) - { - [NSException raise:MGLResourceNotFoundException format: - @"The resource named “%@” could not be found in the Mapbox framework bundle.", imageName]; - } - - return image; -} - - (BOOL)isFullyLoaded { return self.mbglMap.isFullyLoaded(); diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h index 5aa4902a91..e53dc8519c 100644 --- a/platform/ios/src/MGLMapView_Private.h +++ b/platform/ios/src/MGLMapView_Private.h @@ -12,6 +12,9 @@ namespace mbgl { class MGLMapViewImpl; @class MGLSource; +/// Standard animation duration for UI elements. +FOUNDATION_EXTERN const NSTimeInterval MGLAnimationDuration; + /// Minimum size of an annotation’s accessibility element. FOUNDATION_EXTERN const CGSize MGLAnnotationAccessibilityElementMinimumSize; diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 635bda490f..98e673577c 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -15,6 +15,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLCalloutView.h" #import "MGLClockDirectionFormatter.h" #import "MGLCluster.h" +#import "MGLCompassButton.h" #import "MGLCompassDirectionFormatter.h" #import "MGLCoordinateFormatter.h" #import "MGLDistanceFormatter.h" diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h index 22bb740242..55835c50d6 100644 --- a/platform/ios/src/UIImage+MGLAdditions.h +++ b/platform/ios/src/UIImage+MGLAdditions.h @@ -1,9 +1,13 @@ #import <UIKit/UIKit.h> +#import <Mapbox/MGLTypes.h> + #include <mbgl/style/image.hpp> NS_ASSUME_NONNULL_BEGIN +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLResourceNotFoundException; + @interface UIImage (MGLAdditions) - (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage; @@ -14,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN - (mbgl::PremultipliedImage)mgl_premultipliedImage; ++ (UIImage *)mgl_resourceImageNamed:(NSString *)imageName; + @end NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm index 884f92e003..9d05abd6ca 100644 --- a/platform/ios/src/UIImage+MGLAdditions.mm +++ b/platform/ios/src/UIImage+MGLAdditions.mm @@ -1,7 +1,10 @@ #import "UIImage+MGLAdditions.h" +#import "NSBundle+MGLAdditions.h" #include <mbgl/util/image+MGLAdditions.hpp> +const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundException"; + @implementation UIImage (MGLAdditions) - (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage @@ -42,7 +45,20 @@ float(self.scale), isTemplate); } --(mbgl::PremultipliedImage)mgl_premultipliedImage { +- (mbgl::PremultipliedImage)mgl_premultipliedImage { return MGLPremultipliedImageFromCGImage(self.CGImage); } + ++ (UIImage *)mgl_resourceImageNamed:(NSString *)imageName { + UIImage *image = [UIImage imageNamed:imageName + inBundle:[NSBundle mgl_frameworkBundle] + compatibleWithTraitCollection:nil]; + + if (!image) { + [NSException raise:MGLResourceNotFoundException format:@"The resource named “%@” could not be found in the Mapbox framework bundle.", imageName]; + } + + return image; +} + @end |