From 4f90d7a56608d6962cfb86505dfeba92b8cd3154 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Wed, 17 May 2017 15:46:27 -0700 Subject: [ios] Telemetry button in modal view controllers (#9027) Fixes #8980. --- platform/ios/src/MGLMapView.mm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index a4c9f5194c..4054099dbd 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1999,9 +1999,10 @@ public: }]; [alertController addAction:participateAction]; - [self.window.rootViewController presentViewController:alertController - animated:YES - completion:NULL]; + UIViewController *viewController = [self.window.rootViewController mgl_topMostViewController]; + [viewController presentViewController:alertController + animated:YES + completion:NULL]; } #pragma mark - Properties - -- cgit v1.2.1 From 3b109c8540ebd4da943e81beff5d9ae1483500c3 Mon Sep 17 00:00:00 2001 From: Fredrik Karlsson Date: Fri, 19 May 2017 14:01:35 +0200 Subject: Observe layout guides (#7716) * [ios] observe layout guides * [ios] update changelog --- platform/ios/src/MGLMapView.mm | 218 +++++++++++++++++++--------------------- platform/ios/src/MGLScaleBar.mm | 6 ++ 2 files changed, 110 insertions(+), 114 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 4054099dbd..5acb600797 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -131,6 +131,9 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1; const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10); +// Context for KVO observing UILayoutGuides. +static void * MGLLayoutGuidesUpdatedContext = &MGLLayoutGuidesUpdatedContext; + /// Unique identifier representing a single annotation in mbgl. typedef uint32_t MGLAnnotationTag; @@ -233,13 +236,9 @@ public: @property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints; @property (nonatomic, readwrite) UIImageView *compassView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints; @property (nonatomic, readwrite) UIImageView *logoView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; @property (nonatomic, readwrite) MGLStyle *style; @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @@ -294,7 +293,8 @@ public: NSDate *_userLocationAnimationCompletionDate; /// True if a willChange notification has been issued for shape annotation layers and a didChange notification is pending. BOOL _isChangingAnnotationLayers; - + BOOL _isObservingTopLayoutGuide; + BOOL _isObservingBottomLayoutGuide; BOOL _isWaitingForRedundantReachableNotification; BOOL _isTargetingInterfaceBuilder; @@ -471,9 +471,7 @@ public: _logoView = [[UIImageView alloc] initWithImage:logo]; _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); - _logoView.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_logoView]; - _logoViewConstraints = [NSMutableArray array]; // setup attribution // @@ -481,9 +479,7 @@ public: _attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label"); _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint"); [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside]; - _attributionButton.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_attributionButton]; - _attributionButtonConstraints = [NSMutableArray array]; [_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL]; // setup compass @@ -495,16 +491,12 @@ public: _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; [self addSubview:_compassView]; - _compassViewConstraints = [NSMutableArray array]; // setup scale control // _scaleBar = [[MGLScaleBar alloc] init]; - _scaleBar.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_scaleBar]; - _scaleBarConstraints = [NSMutableArray array]; // setup interaction // @@ -672,6 +664,14 @@ public: [[NSNotificationCenter defaultCenter] removeObserver:self]; [_attributionButton removeObserver:self forKeyPath:@"hidden"]; + if (_isObservingTopLayoutGuide) { + [(NSObject *)self.viewControllerForLayoutGuides.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + } + + if (_isObservingBottomLayoutGuide) { + [(NSObject *)self.viewControllerForLayoutGuides.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + } + // Removing the annotations unregisters any outstanding KVO observers. NSArray *annotations = self.annotations; if (annotations) @@ -697,11 +697,6 @@ public: { [EAGLContext setCurrentContext:nil]; } - - [self.logoViewConstraints removeAllObjects]; - self.logoViewConstraints = nil; - [self.attributionButtonConstraints removeAllObjects]; - self.attributionButtonConstraints = nil; } - (void)setDelegate:(nullable id)delegate @@ -786,105 +781,31 @@ public: - (void)updateConstraints { - // scale control - // - [self removeConstraints:self.scaleBarConstraints]; - [self.scaleBarConstraints removeAllObjects]; + [super updateConstraints]; - [self.scaleBarConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.scaleBar - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop - multiplier:1 - constant:5+self.contentInset.top]]; + // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets + // is set to YES, -[MGLMapView adjustContentInset] takes top and bottom layout + // guides into account. To get notified about changes to the layout guides, + // we need to observe their bounds and re-layout accordingly. - [self.scaleBarConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.scaleBar - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeading - multiplier:1 - constant:8 + self.contentInset.left]]; + UIViewController *viewController = self.viewControllerForLayoutGuides; + BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; - [self addConstraints:self.scaleBarConstraints]; + if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = YES; + } else if (!useLayoutGuides && _isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; + } - // compass - // - [self removeConstraints:self.compassViewConstraints]; - [self.compassViewConstraints removeAllObjects]; - - [self.compassViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.compassView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop - multiplier:1 - constant:5 + self.contentInset.top]]; - - [self.compassViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.compassView - attribute:NSLayoutAttributeTrailing - multiplier:1 - constant:5 + self.contentInset.right]]; - - [self addConstraints:self.compassViewConstraints]; - - // logo bug - // - [self removeConstraints:self.logoViewConstraints]; - [self.logoViewConstraints removeAllObjects]; - - [self.logoViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.logoView - attribute:NSLayoutAttributeBaseline - multiplier:1 - constant:8 + self.contentInset.bottom]]; - - [self.logoViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.logoView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeading - multiplier:1 - constant:8 + self.contentInset.left]]; - [self addConstraints:self.logoViewConstraints]; - - // attribution button - // - [self removeConstraints:self.attributionButtonConstraints]; - [self.attributionButtonConstraints removeAllObjects]; - - [self.attributionButtonConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.attributionButton - attribute:NSLayoutAttributeBaseline - multiplier:1 - constant:8 + self.contentInset.bottom]]; - - [self.attributionButtonConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.attributionButton - attribute:NSLayoutAttributeTrailing - multiplier:1 - constant:8 + self.contentInset.right]]; - [self addConstraints:self.attributionButtonConstraints]; - - [super updateConstraints]; + if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = YES; + } else if (!useLayoutGuides && _isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; + } } - (BOOL)isOpaque @@ -917,6 +838,10 @@ public: [super layoutSubviews]; [self adjustContentInset]; + + [self observeLayoutGuidesIfNeeded]; + + [self layoutOrnaments]; if (!_isTargetingInterfaceBuilder) { _mbglMap->setSize([self size]); @@ -931,6 +856,39 @@ public: [self updateUserLocationAnnotationView]; } +- (void)layoutOrnaments +{ + // scale bar + self.scaleBar.frame = { + self.contentInset.left+8, + self.contentInset.top+5, + CGRectGetWidth(self.scaleBar.frame), + CGRectGetHeight(self.scaleBar.frame) + }; + + // compass + self.compassView.center = { + .x = CGRectGetWidth(self.bounds)-CGRectGetMidX(self.compassView.bounds)-self.contentInset.right-5, + .y = CGRectGetMidY(self.compassView.bounds)+self.contentInset.top+5 + }; + + // logo bug + self.logoView.frame = { + self.contentInset.left+5, + CGRectGetHeight(self.bounds)-5-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), + CGRectGetWidth(self.logoView.bounds), + CGRectGetHeight(self.logoView.bounds) + }; + + // attribution + self.attributionButton.frame = { + CGRectGetWidth(self.bounds)-CGRectGetWidth(self.attributionButton.bounds)-self.contentInset.right-8, + CGRectGetHeight(self.bounds)-CGRectGetHeight(self.attributionButton.bounds)-self.contentInset.bottom-8, + CGRectGetWidth(self.attributionButton.bounds), + CGRectGetHeight(self.attributionButton.bounds) + }; +} + /// Updates `contentInset` to reflect the current window geometry. - (void)adjustContentInset { @@ -970,6 +928,34 @@ public: self.contentInset = contentInset; } +- (void)observeLayoutGuidesIfNeeded +{ + UIViewController *viewController = self.viewControllerForLayoutGuides; + BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; + + if (!_isObservingTopLayoutGuide && useLayoutGuides && viewController.topLayoutGuide) + { + [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = YES; + } + else if (!useLayoutGuides && _isObservingTopLayoutGuide) + { + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; + } + + if (!_isObservingBottomLayoutGuide && useLayoutGuides && viewController.bottomLayoutGuide) + { + [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = YES; + } + else if (!useLayoutGuides && _isObservingBottomLayoutGuide) + { + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; + } +} + - (void)setContentInset:(UIEdgeInsets)contentInset { [self setContentInset:contentInset animated:NO]; @@ -1000,7 +986,7 @@ public: } // Compass, logo and attribution button constraints needs to be updated. - [self setNeedsUpdateConstraints]; + [self setNeedsLayout]; } /// Returns the frame of inset content within the map view. @@ -2066,6 +2052,10 @@ public: [self updateCalloutView]; } } + else if (context == MGLLayoutGuidesUpdatedContext && [keyPath isEqualToString:@"bounds"]) + { + [self setNeedsLayout]; + } } + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 1216e400b3..cd88c1e08e 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -220,6 +220,12 @@ static const CGFloat MGLFeetPerMeter = 3.28084; 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]; } -- cgit v1.2.1 From 554b1cf3e2c8ba21fd3e2259d04915811668a3ac Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Fri, 19 May 2017 09:35:09 -0700 Subject: [ios] Add annotation view initializer with annotation and reuse id (#9029) --- platform/ios/src/MGLAnnotationView.h | 30 ++++++++++++++++++++++++++++++ platform/ios/src/MGLAnnotationView.mm | 18 +++++++++++------- 2 files changed, 41 insertions(+), 7 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 184efdb324..9b17f05a6e 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -73,6 +73,36 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { */ - (instancetype)initWithReuseIdentifier:(nullable NSString *)reuseIdentifier; +/** + Initializes and returns a new annotation view object. + + Providing an annotation allows you to explicitly associate the annotation instance + with the new view and, in custom subclasses of `MGLAnnotationView`, customize the view + based on properties of the annotation instance in an overridden initializer. However, + annotation views that are reused will not necessarily be associated with the + same annotation they were initialized with. Also, annotation views that are in + the reuse queue will have a nil value for the annotation property. Passing an annotation + instance to the view is optional and the map view will automatically associate annotations + with views when views are provided to the map via the `-[MGLMapViewDelegate mapView:viewForAnnotation:]` + method. + + The reuse identifier provides a way for you to improve performance by recycling + annotation views as they enter and leave the map’s viewport. As an annotation + leaves the viewport, the map view moves its associated view to a reuse queue. + When a new annotation becomes visible, you can request a view for that + annotation by passing the appropriate reuse identifier string to the + `-[MGLMapView dequeueReusableAnnotationViewWithIdentifier:]` method. + + @param annotation The annotation object to associate with the new view. + @param reuseIdentifier A unique string identifier for this view that allows you + to reuse this view with multiple similar annotations. You can set this + parameter to `nil` if you don’t intend to reuse the view, but it is a good + idea in general to specify a reuse identifier to avoid creating redundant + views. + @return The initialized annotation view object. + */ +- (instancetype)initWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier; + /** Called when the view is removed from the reuse queue. diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 5e0ae3b848..9e1212b4fb 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -19,12 +19,20 @@ @implementation MGLAnnotationView -- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier -{ - self = [self initWithFrame:CGRectZero]; ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { + return [self initWithAnnotation:nil reuseIdentifier:reuseIdentifier]; +} + +- (instancetype)initWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { + self = [super initWithFrame:CGRectZero]; if (self) { _lastAppliedScaleTransform = CATransform3DIdentity; + _annotation = annotation; _reuseIdentifier = [reuseIdentifier copy]; _scalesWithViewingDistance = YES; _enabled = YES; @@ -32,10 +40,6 @@ return self; } -+ (BOOL)supportsSecureCoding { - return YES; -} - - (instancetype)initWithCoder:(NSCoder *)decoder { if (self = [super initWithCoder:decoder]) { _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; -- cgit v1.2.1 From b9d3ccc99506efcadd90bc6b1fc4b9831dfd32df Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Fri, 19 May 2017 11:27:46 -0700 Subject: [ios] Remove annotation view from container view when annotation removed (#9025) The annotation container view keeps an array of annotation views that is separate from the array of subviews that is a property of the UIView parent class. This removes an annotation view from that container view array when the associated annotation is removed. This avoids issue related to previously removed annotation views continuing to be involved in map view logic around annotation view selection due to taps. --- platform/ios/src/MGLMapView.mm | 1 + 1 file changed, 1 insertion(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 5acb600797..720c1d506a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3492,6 +3492,7 @@ public: annotationView.annotation = nil; [annotationView removeFromSuperview]; + [self.annotationContainerView.annotationViews removeObject:annotationView]; if (annotationTag == _selectedAnnotationTag) { -- cgit v1.2.1 From e52249c17fb529c476569b6d7ef141be1bff7d7f Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Tue, 23 May 2017 10:59:24 -0400 Subject: [ios, macos] Light property implementation in MGLStyle (#9043) * [ios, macos] Add MGLLight to MGLStyle * [ios, macos] Implement Objc bindings for Light object * [ios, macos] Remove rawLight from MGLLight and re-implement it as value class * [ios, macos] Fix build on macos * [ios, macos] Add MGLLight documentation, Move MGLLightPosition to MGLLight * [ios, macos] Add MGLLight tests. * [ios, macos] Update changelogs * [ios, macos] Fix misspelling * [ios, macos] Fix MGLLightAnchor enum property names * [ios, macos] Update documentation. Improve varialble naming. * [ios, macos] Rename MGLLightPosition to MGLSphericalPosition * [ios, macos] Update data types of MGLSphericalPosition --- platform/ios/src/Mapbox.h | 1 + 1 file changed, 1 insertion(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 9a9dc702ca..67a26e8ed4 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -19,6 +19,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLDistanceFormatter.h" #import "MGLFeature.h" #import "MGLGeometry.h" +#import "MGLLight.h" #import "MGLMapCamera.h" #import "MGLMapView.h" #import "MGLMapView+IBAdditions.h" -- cgit v1.2.1 From 9b11bb98fa855da9d845ed01e59451943bb29af4 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 23 May 2017 13:24:16 -0400 Subject: [ios] Fallback to Mapbox.bundle as the framework bundle (#9074) Fixes an issue where localizations could not be found when using static builds. Throws exception if our bundle can't be found. --- platform/ios/src/MGLAPIClient.m | 2 +- platform/ios/src/MGLMapView.mm | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m index 22ee5c55f5..124d436197 100644 --- a/platform/ios/src/MGLAPIClient.m +++ b/platform/ios/src/MGLAPIClient.m @@ -117,7 +117,7 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; - (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource { NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle]; - NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der" inDirectory:frameworkBundle.mgl_resourcesDirectory]; + NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der"]; if (cerPath != nil) { *certificate = [NSData dataWithContentsOfFile:cerPath]; } diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 720c1d506a..9a27cf24c6 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -5293,8 +5293,7 @@ public: NSString *extension = imageName.pathExtension.length ? imageName.pathExtension : @"png"; NSBundle *bundle = [NSBundle mgl_frameworkBundle]; NSString *path = [bundle pathForResource:imageName.stringByDeletingPathExtension - ofType:extension - inDirectory:bundle.mgl_resourcesDirectory]; + ofType:extension]; if ( ! path) { [NSException raise:@"Resource not found" format: -- cgit v1.2.1 From d90c1722c9718e2532d406c13b17edf988070e2e Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 19 May 2017 13:08:21 -0400 Subject: [ios] Move image resources to an asset catalog & switch to PDFs --- platform/ios/src/MGLMapView.mm | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9a27cf24c6..ed989459f5 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -467,7 +467,7 @@ public: // setup logo bug // - UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox.png"]; + UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox"]; _logoView = [[UIImageView alloc] initWithImage:logo]; _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); @@ -616,11 +616,11 @@ public: - (UIImage *)compassImage { - UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass.png"]; + UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass"]; UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale); [scaleImage drawInRect:{ CGPointZero, scaleImage.size }]; - CGFloat northSize = 9; + CGFloat northSize = 11; UIFont *northFont; if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { @@ -635,7 +635,7 @@ public: NSForegroundColorAttributeName: [UIColor whiteColor], }]; CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2, - scaleImage.size.height * 0.45, + scaleImage.size.height * 0.435, north.size.width, north.size.height); [north drawInRect:stringRect]; @@ -874,8 +874,8 @@ public: // logo bug self.logoView.frame = { - self.contentInset.left+5, - CGRectGetHeight(self.bounds)-5-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), + self.contentInset.left+8, + CGRectGetHeight(self.bounds)-8-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), CGRectGetWidth(self.logoView.bounds), CGRectGetHeight(self.logoView.bounds) }; @@ -5290,17 +5290,17 @@ public: + (UIImage *)resourceImageNamed:(NSString *)imageName { - NSString *extension = imageName.pathExtension.length ? imageName.pathExtension : @"png"; - NSBundle *bundle = [NSBundle mgl_frameworkBundle]; - NSString *path = [bundle pathForResource:imageName.stringByDeletingPathExtension - ofType:extension]; - if ( ! path) + UIImage *image = [UIImage imageNamed:imageName + inBundle:[NSBundle mgl_frameworkBundle] + compatibleWithTraitCollection:nil]; + + if ( ! image) { - [NSException raise:@"Resource not found" format: + [NSException raise:@"MGLResourceNotFoundException" format: @"The resource named “%@” could not be found in the Mapbox framework bundle.", imageName]; } - return [UIImage imageWithContentsOfFile:path]; + return image; } - (BOOL)isFullyLoaded -- cgit v1.2.1 From fce7747da099519d8e29eff4befc5c205048492a Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Wed, 24 May 2017 21:08:19 -0700 Subject: [ios] Fix annotation initializers for subclasses of MGLAnnotationView (#9104) Use a common init function in both of the provided initializers so that subclasses of `MGLAnnotationView` written in Swift don't need to override `init(annotation, reuseIdentifier)` --- platform/ios/src/MGLAnnotationView.mm | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 9e1212b4fb..78e38f10cb 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -24,22 +24,29 @@ } - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { - return [self initWithAnnotation:nil reuseIdentifier:reuseIdentifier]; + self = [super initWithFrame:CGRectZero]; + if (self) { + [self commonInitWithAnnotation:nil reuseIdentifier:reuseIdentifier]; + } + return self; } - (instancetype)initWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { self = [super initWithFrame:CGRectZero]; - if (self) - { - _lastAppliedScaleTransform = CATransform3DIdentity; - _annotation = annotation; - _reuseIdentifier = [reuseIdentifier copy]; - _scalesWithViewingDistance = YES; - _enabled = YES; + if (self) { + [self commonInitWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; } return self; } +- (void)commonInitWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { + _lastAppliedScaleTransform = CATransform3DIdentity; + _annotation = annotation; + _reuseIdentifier = [reuseIdentifier copy]; + _scalesWithViewingDistance = YES; + _enabled = YES; +} + - (instancetype)initWithCoder:(NSCoder *)decoder { if (self = [super initWithCoder:decoder]) { _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; -- cgit v1.2.1 From 7b85bcccae5c7b8f3b82135362e16a799413fcc5 Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 30 May 2017 10:51:07 -0700 Subject: [ios] Remove filter of single metric event --- platform/ios/src/MGLMapboxEvents.m | 7 ------- 1 file changed, 7 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m index 7b28ccf1a8..4f1413d300 100644 --- a/platform/ios/src/MGLMapboxEvents.m +++ b/platform/ios/src/MGLMapboxEvents.m @@ -317,13 +317,6 @@ const NSTimeInterval MGLFlushInterval = 180; return; } - if ([self.eventQueue count] <= 1) { - [self.eventQueue removeAllObjects]; - [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; - _backgroundTaskIdentifier = UIBackgroundTaskInvalid; - return; - } - NSArray *events = [NSArray arrayWithArray:self.eventQueue]; [self.eventQueue removeAllObjects]; -- cgit v1.2.1 From 608fd66ab833937671a52d1366caf11a23f7c457 Mon Sep 17 00:00:00 2001 From: Fredrik Karlsson Date: Thu, 25 May 2017 21:42:28 +0200 Subject: [ios] remove layout guide observers --- platform/ios/src/MGLMapView.mm | 100 ++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 56 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index ed989459f5..39a9281a7a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -656,21 +656,57 @@ public: _isWaitingForRedundantReachableNotification = NO; } -- (void)dealloc +- (void)willMoveToWindow:(UIWindow *)newWindow { - [_reachability stopNotifier]; + [super willMoveToWindow:newWindow]; + + if (newWindow) { + [self addLayoutGuideObserversIfNeeded]; + } else { + [self removeLayoutGuideObserversIfNeeded]; + } +} - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [_attributionButton removeObserver:self forKeyPath:@"hidden"]; +- (void)addLayoutGuideObserversIfNeeded +{ + UIViewController *viewController = self.viewControllerForLayoutGuides; + BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; + + if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = YES; + } + + if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = YES; + } +} +- (void)removeLayoutGuideObserversIfNeeded +{ + UIViewController *viewController = self.viewControllerForLayoutGuides; + if (_isObservingTopLayoutGuide) { - [(NSObject *)self.viewControllerForLayoutGuides.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; } if (_isObservingBottomLayoutGuide) { - [(NSObject *)self.viewControllerForLayoutGuides.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; } +} + +- (void)dealloc +{ + [_reachability stopNotifier]; + + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_attributionButton removeObserver:self forKeyPath:@"hidden"]; + + [self removeLayoutGuideObserversIfNeeded]; // Removing the annotations unregisters any outstanding KVO observers. NSArray *annotations = self.annotations; @@ -787,25 +823,7 @@ public: // is set to YES, -[MGLMapView adjustContentInset] takes top and bottom layout // guides into account. To get notified about changes to the layout guides, // we need to observe their bounds and re-layout accordingly. - - UIViewController *viewController = self.viewControllerForLayoutGuides; - BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; - - if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) { - [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = YES; - } else if (!useLayoutGuides && _isObservingTopLayoutGuide) { - [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = NO; - } - - if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { - [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = YES; - } else if (!useLayoutGuides && _isObservingBottomLayoutGuide) { - [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = NO; - } + [self addLayoutGuideObserversIfNeeded]; } - (BOOL)isOpaque @@ -839,8 +857,6 @@ public: [self adjustContentInset]; - [self observeLayoutGuidesIfNeeded]; - [self layoutOrnaments]; if (!_isTargetingInterfaceBuilder) { @@ -928,34 +944,6 @@ public: self.contentInset = contentInset; } -- (void)observeLayoutGuidesIfNeeded -{ - UIViewController *viewController = self.viewControllerForLayoutGuides; - BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; - - if (!_isObservingTopLayoutGuide && useLayoutGuides && viewController.topLayoutGuide) - { - [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = YES; - } - else if (!useLayoutGuides && _isObservingTopLayoutGuide) - { - [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = NO; - } - - if (!_isObservingBottomLayoutGuide && useLayoutGuides && viewController.bottomLayoutGuide) - { - [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = YES; - } - else if (!useLayoutGuides && _isObservingBottomLayoutGuide) - { - [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = NO; - } -} - - (void)setContentInset:(UIEdgeInsets)contentInset { [self setContentInset:contentInset animated:NO]; -- cgit v1.2.1 From a39116a552bbb7b77f2946b5931944c7065d818e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 30 May 2017 15:14:00 -0700 Subject: =?UTF-8?q?Migrate=20to=20GL=20JS=E2=80=93powered=20feedback=20for?= =?UTF-8?q?m=20(#9078)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ios, macos] Updated feedback URL * [ios, macos] Add referrer, heading, pitch to feedback URL * [ios, macos] Updated changelogs for feedback changes * [ios] Vary referrer by platform --- platform/ios/src/MGLMapView.mm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 39a9281a7a..9d4c295d62 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -64,7 +64,7 @@ #import "MGLCompactCalloutView.h" #import "MGLAnnotationContainerView.h" #import "MGLAnnotationContainerView_Private.h" -#import "MGLAttributionInfo.h" +#import "MGLAttributionInfo_Private.h" #include #include @@ -1895,8 +1895,12 @@ public: { if (info.feedbackLink) { - url = [info feedbackURLAtCenterCoordinate:self.centerCoordinate - zoomLevel:self.zoomLevel]; + MGLMapCamera *camera = self.camera; + url = [info feedbackURLForStyleURL:self.styleURL + atCenterCoordinate:camera.centerCoordinate + zoomLevel:self.zoomLevel + direction:camera.heading + pitch:camera.pitch]; } [[UIApplication sharedApplication] openURL:url]; } -- cgit v1.2.1 From 012e88cc9b069f62824d9072ca3ede6a3c37d04b Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Thu, 1 Jun 2017 10:53:02 -0700 Subject: [ios] Make annotation view rotation alignment configurable (#9147) This commit adds `rotatesWithMap` property on `MGLAnnotationView`. This property, when set to `YES` fixes the annotation to a map such that view follows map's rotation angle. This is useful when user wants to display rotation-dependent annotations (e.g. sector lights). --- platform/ios/src/MGLAnnotationView.h | 13 +++++++++++++ platform/ios/src/MGLAnnotationView.mm | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 9b17f05a6e..2802d31b05 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -171,6 +171,19 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { */ @property (nonatomic, assign) BOOL scalesWithViewingDistance; +/** + A Boolean value that determines whether the annotation view rotates together + with the map. + + When the value of this property is `YES` and the map is rotated, the annotation + view rotates. This is also the behavior of `MGLAnnotationImage` objects. When the + value of this property is `NO` the annotation has its rotation angle fixed. + + The default value of this property is `NO`. Set this property to `YES` if the + view’s rotation is important. + */ +@property (nonatomic, assign) BOOL rotatesToMatchCamera; + #pragma mark Managing the Selection State /** diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 78e38f10cb..94d0649413 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -11,6 +11,7 @@ @property (nonatomic, readwrite, nullable) NSString *reuseIdentifier; @property (nonatomic, readwrite) CATransform3D lastAppliedScaleTransform; +@property (nonatomic, readwrite) CATransform3D lastAppliedRotateTransform; @property (nonatomic, weak) UIPanGestureRecognizer *panGestureRecognizer; @property (nonatomic, weak) UILongPressGestureRecognizer *longPressRecognizer; @property (nonatomic, weak) MGLMapView *mapView; @@ -53,6 +54,7 @@ _annotation = [decoder decodeObjectOfClass:[NSObject class] forKey:@"annotation"]; _centerOffset = [decoder decodeCGVectorForKey:@"centerOffset"]; _scalesWithViewingDistance = [decoder decodeBoolForKey:@"scalesWithViewingDistance"]; + _rotatesToMatchCamera = [decoder decodeBoolForKey:@"rotatesToMatchCamera"]; _selected = [decoder decodeBoolForKey:@"selected"]; _enabled = [decoder decodeBoolForKey:@"enabled"]; self.draggable = [decoder decodeBoolForKey:@"draggable"]; @@ -66,6 +68,7 @@ [coder encodeObject:_annotation forKey:@"annotation"]; [coder encodeCGVector:_centerOffset forKey:@"centerOffset"]; [coder encodeBool:_scalesWithViewingDistance forKey:@"scalesWithViewingDistance"]; + [coder encodeBool:_rotatesToMatchCamera forKey:@"rotatesToMatchCamera"]; [coder encodeBool:_selected forKey:@"selected"]; [coder encodeBool:_enabled forKey:@"enabled"]; [coder encodeBool:_draggable forKey:@"draggable"]; @@ -109,6 +112,7 @@ super.center = center; [self updateScaleTransformForViewingDistance]; + [self updateRotateTransform]; } - (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance @@ -157,6 +161,26 @@ } } +- (void)setRotatesToMatchCamera:(BOOL)rotatesToMatchCamera +{ + if (_rotatesToMatchCamera != rotatesToMatchCamera) + { + _rotatesToMatchCamera = rotatesToMatchCamera; + [self updateRotateTransform]; + } +} + +- (void)updateRotateTransform +{ + if (self.rotatesToMatchCamera == NO) return; + + CGFloat directionRad = self.mapView.direction * M_PI / 180.0; + CATransform3D newRotateTransform = CATransform3DMakeRotation(-directionRad, 0, 0, 1); + self.layer.transform = CATransform3DConcat(CATransform3DIdentity, newRotateTransform); + + _lastAppliedRotateTransform = newRotateTransform; +} + #pragma mark - Draggable - (void)setDraggable:(BOOL)draggable -- cgit v1.2.1 From df3af9738962d377b0b269347a9d91f2173da7e5 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Wed, 14 Jun 2017 11:26:34 -0700 Subject: [ios, macos] Revised descriptions for abstract classes (#9095) Addresses https://github.com/mapbox/mapbox-gl-native/issues/8635 --- platform/ios/src/MGLUserLocation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h index c41c3ee7fd..30bfc592ca 100644 --- a/platform/ios/src/MGLUserLocation.h +++ b/platform/ios/src/MGLUserLocation.h @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN /** The MGLUserLocation class defines a specific type of annotation that identifies the user’s current location. You do not create instances of this class - directly. Instead, you retrieve an existing MGLUserLocation object from the + directly. Instead, you retrieve an existing `MGLUserLocation` object from the `userLocation` property of the map view displayed in your application. */ @interface MGLUserLocation : NSObject -- cgit v1.2.1 From 505999a52df625c21791eae352342ae25e9c48af Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Mon, 19 Jun 2017 09:34:44 -1000 Subject: [ios] Update telemetry cert pinning (#9292) --- platform/ios/src/MGLAPIClient.m | 114 +++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 65 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m index 124d436197..8a987d76d8 100644 --- a/platform/ios/src/MGLAPIClient.m +++ b/platform/ios/src/MGLAPIClient.m @@ -17,8 +17,10 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; @property (nonatomic, copy) NSURLSession *session; @property (nonatomic, copy) NSURL *baseURL; -@property (nonatomic, copy) NSData *digicertCert; -@property (nonatomic, copy) NSData *geoTrustCert; +@property (nonatomic, copy) NSData *digicertCert_2016; +@property (nonatomic, copy) NSData *geoTrustCert_2016; +@property (nonatomic, copy) NSData *digicertCert_2017; +@property (nonatomic, copy) NSData *geoTrustCert_2017; @property (nonatomic, copy) NSData *testServerCert; @property (nonatomic, copy) NSString *userAgent; @property (nonatomic) BOOL usesTestServer; @@ -107,10 +109,14 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; - (void)loadCertificates { NSData *certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust"]; - self.geoTrustCert = certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert"]; - self.digicertCert = certificate; + [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2016"]; + self.geoTrustCert_2016 = certificate; + [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2016"]; + self.digicertCert_2016 = certificate; + [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2017"]; + self.geoTrustCert_2017 = certificate; + [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2017"]; + self.digicertCert_2017 = certificate; [self loadCertificate:&certificate withResource:@"api_mapbox_staging"]; self.testServerCert = certificate; } @@ -141,75 +147,53 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; #pragma mark NSURLSessionDelegate +- (BOOL)evaluateCertificateWithCertificateData:(NSData *)certificateData keyCount:(CFIndex)keyCount serverTrust:(SecTrustRef)serverTrust challenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { + for (int lc = 0; lc < keyCount; lc++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); + NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); + if ([remoteCertificateData isEqualToData:certificateData]) { + completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + return YES; + } + } + return NO; +} + - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { + if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; SecTrustResultType trustResult; - - // Validate the certificate chain with the device's trust store anyway - // This *might* give use revocation checking + + // Validate the certificate chain with the device's trust store anyway this *might* use revocation checking SecTrustEvaluate(serverTrust, &trustResult); - if (trustResult == kSecTrustResultUnspecified) - { + + BOOL found = NO; // For clarity; we start in a state where the challange has not been completed and no certificate has been found + + if (trustResult == kSecTrustResultUnspecified) { // Look for a pinned certificate in the server's certificate chain - long numKeys = SecTrustGetCertificateCount(serverTrust); - - BOOL found = NO; - // Try GeoTrust Cert First - for (int lc = 0; lc < numKeys; lc++) { - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); - NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); - - // Compare Remote Key With Local Version - if ([remoteCertificateData isEqualToData:_geoTrustCert]) { - // Found the certificate; continue connecting - completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - found = YES; - break; - } + CFIndex numKeys = SecTrustGetCertificateCount(serverTrust); + + // Check certs in the following order: digicert 2016, digicert 2017, geotrust 2016, geotrust 2017 + found = [self evaluateCertificateWithCertificateData:self.digicertCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; + if (!found) { + found = [self evaluateCertificateWithCertificateData:self.digicertCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; } - if (!found) { - // Fallback to Digicert Cert - for (int lc = 0; lc < numKeys; lc++) { - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); - NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); - - // Compare Remote Key With Local Version - if ([remoteCertificateData isEqualToData:_digicertCert]) { - // Found the certificate; continue connecting - completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - found = YES; - break; - } - } - - if (!found && _usesTestServer) { - // See if this is test server - for (int lc = 0; lc < numKeys; lc++) { - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); - NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); - - // Compare Remote Key With Local Version - if ([remoteCertificateData isEqualToData:_testServerCert]) { - // Found the certificate; continue connecting - completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - found = YES; - break; - } - } - } - - if (!found) { - // The certificate wasn't found in GeoTrust nor Digicert. Cancel the connection. - completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } + found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; + } + if (!found) { + found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; + } + + // If challenge can't be completed with any of the above certs, then try the test server if the app is configured to use the test server + if (!found && _usesTestServer) { + found = [self evaluateCertificateWithCertificateData:self.testServerCert keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; } } - else - { - // Certificate chain validation failed; cancel the connection + + if (!found) { + // No certificate was found so cancel the connection. completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } } -- cgit v1.2.1 From d3730241bbc98ec9a44295e3d3f1e7318d4cf73e Mon Sep 17 00:00:00 2001 From: Romain Quidet Date: Thu, 22 Jun 2017 15:50:09 +0200 Subject: 7910: cancel tracking if ongoing animation is stopped manually (#7916) * 7910: cancel tracking if ongoing animation is stopped manually * 7910 updating change log * [ios] Fix map camera animation when a significant change occurs * [ios] Update cancel tracking documentation. --- platform/ios/src/MGLMapView.mm | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9d4c295d62..459eda19c8 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -84,6 +84,8 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) { MGLUserTrackingStatePossible = 0, /// The map view has begun to move to the first reported user location. MGLUserTrackingStateBegan, + /// The map view begins a significant transition. + MGLUserTrackingStateBeginSignificantTransition, /// The map view has finished moving to the first reported user location. MGLUserTrackingStateChanged, }; @@ -112,6 +114,9 @@ const NSUInteger MGLTargetFrameInterval = 1; // Target FPS will be 60 divided b /// Tolerance for snapping to true north, measured in degrees in either direction. const CLLocationDirection MGLToleranceForSnappingToNorth = 7; +/// Distance threshold to stop the camera while animating. +const CLLocationDistance MGLDistanceThresholdForCameraPause = 500; + /// Reuse identifier and file name of the default point annotation image. static NSString * const MGLDefaultStyleMarkerSymbolName = @"default_marker"; @@ -309,6 +314,8 @@ public: /// Center coordinate of the pinch gesture on the previous iteration of the gesture. CLLocationCoordinate2D _previousPinchCenterCoordinate; NSUInteger _previousPinchNumberOfTouches; + + CLLocationDistance _distanceFromOldUserLocation; BOOL _delegateHasAlphasForShapeAnnotations; BOOL _delegateHasStrokeColorsForShapeAnnotations; @@ -1166,6 +1173,10 @@ public: { _changeDelimiterSuppressionDepth = 0; _mbglMap->setGestureInProgress(false); + if (self.userTrackingState == MGLUserTrackingStateBegan) + { + [self setUserTrackingMode:MGLUserTrackingModeNone animated:NO]; + } _mbglMap->cancelTransitions(); } @@ -4385,6 +4396,7 @@ public: { CLLocation *oldLocation = self.userLocation.location; CLLocation *newLocation = locations.lastObject; + _distanceFromOldUserLocation = [newLocation distanceFromLocation:oldLocation]; if ( ! _showsUserLocation || ! newLocation || ! CLLocationCoordinate2DIsValid(newLocation.coordinate)) return; @@ -4478,7 +4490,12 @@ public: /// first location update. - (void)didUpdateLocationSignificantlyAnimated:(BOOL)animated { - self.userTrackingState = MGLUserTrackingStateBegan; + + if (_distanceFromOldUserLocation >= MGLDistanceThresholdForCameraPause) { + self.userTrackingState = MGLUserTrackingStateBeginSignificantTransition; + } else { + self.userTrackingState = MGLUserTrackingStateBegan; + } MGLMapCamera *camera = self.camera; camera.centerCoordinate = self.userLocation.location.coordinate; @@ -4498,7 +4515,8 @@ public: peakAltitude:-1 completionHandler:^{ MGLMapView *strongSelf = weakSelf; - if (strongSelf.userTrackingState == MGLUserTrackingStateBegan) + if (strongSelf.userTrackingState == MGLUserTrackingStateBegan || + strongSelf.userTrackingState == MGLDistanceThresholdForCameraPause) { strongSelf.userTrackingState = MGLUserTrackingStateChanged; } -- cgit v1.2.1 From eedd0553b467bb82d5e2e928943beb12522d056c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Thu, 22 Jun 2017 11:09:09 -0700 Subject: [ios] Allow delegate to keep wandering pinch from panning map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MGLMapView consults MGLMapViewDelegate about whether to zoom the map in response to a pinch gesture, but it should also account for the delegate’s response when panning the map due to the pinch’s center point wandering. Fixes #9168. --- platform/ios/src/MGLMapView.mm | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 459eda19c8..ba37dc3d31 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1306,16 +1306,16 @@ public: [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) { _mbglMap->setZoom(zoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); - } - // The gesture recognizer only reports the gesture’s current center - // point, so use the previous center point to anchor the transition. - // If the number of touches has changed, the remembered center point is - // meaningless. - if (self.userTrackingMode == MGLUserTrackingModeNone && pinch.numberOfTouches == _previousPinchNumberOfTouches) - { - CLLocationCoordinate2D centerCoordinate = _previousPinchCenterCoordinate; - _mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate), - mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); + // The gesture recognizer only reports the gesture’s current center + // point, so use the previous center point to anchor the transition. + // If the number of touches has changed, the remembered center point is + // meaningless. + if (self.userTrackingMode == MGLUserTrackingModeNone && pinch.numberOfTouches == _previousPinchNumberOfTouches) + { + CLLocationCoordinate2D centerCoordinate = _previousPinchCenterCoordinate; + _mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate), + mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); + } } [self cameraIsChanging]; } -- cgit v1.2.1 From 9b0b74dba6f33b49e2ed009fd0fef2755a944954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Fri, 23 Jun 2017 16:10:04 -0700 Subject: [ios] Fixed infinite loop zooming in past z23 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At zoom levels where the minimum 1 meter or 4 feet would be wider than the scale bar’s maximum width, the local variable holding the preferred row was left undefined. A loop that later iterated based on this row would effectively iterate infinitely until memory pressure forces the system to quit the application. --- platform/ios/src/MGLScaleBar.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index cd88c1e08e..410aa7d57e 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -188,9 +188,9 @@ static const CGFloat MGLFeetPerMeter = 3.28084; - (MGLRow)preferredRow { CLLocationDistance maximumDistance = [self maximumWidth] * [self unitsPerPoint]; - MGLRow row; BOOL useMetric = [self usesMetricSystem]; + MGLRow row = useMetric ? MGLMetricTable[0] : MGLImperialTable[0]; NSUInteger count = useMetric ? sizeof(MGLMetricTable) / sizeof(MGLMetricTable[0]) : sizeof(MGLImperialTable) / sizeof(MGLImperialTable[0]); -- cgit v1.2.1 From de37ae9628045c825ebf9434b87331c69a75f197 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 26 Jun 2017 10:21:00 -0700 Subject: [ios] remove tile cache size customization --- platform/ios/src/MGLMapView.mm | 40 ---------------------------------------- 1 file changed, 40 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 6c6d8d2980..c1d257dd51 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -438,7 +438,6 @@ public: const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _mbglThreadPool = mbgl::sharedThreadPool(); _mbglMap = new mbgl::Map(*_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); - [self validateTileCacheSize]; // start paused if in IB if (_isTargetingInterfaceBuilder || background) { @@ -757,42 +756,6 @@ public: #pragma mark - Layout - -- (void)setFrame:(CGRect)frame -{ - [super setFrame:frame]; - if ( ! CGRectEqualToRect(frame, self.frame)) - { - [self validateTileCacheSize]; - } -} - -- (void)setBounds:(CGRect)bounds -{ - [super setBounds:bounds]; - if ( ! CGRectEqualToRect(bounds, self.bounds)) - { - [self validateTileCacheSize]; - } -} - -- (void)validateTileCacheSize -{ - if ( ! _mbglMap) - { - return; - } - - CGFloat zoomFactor = self.maximumZoomLevel - self.minimumZoomLevel + 1; - CGFloat cpuFactor = [NSProcessInfo processInfo].processorCount; - CGFloat memoryFactor = (CGFloat)[NSProcessInfo processInfo].physicalMemory / 1000 / 1000 / 1000; - CGFloat sizeFactor = (CGRectGetWidth(self.bounds) / mbgl::util::tileSize) * - (CGRectGetHeight(self.bounds) / mbgl::util::tileSize); - - NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5; - - _mbglMap->setSourceTileCacheSize(cacheSize); -} - + (BOOL)requiresConstraintBasedLayout { return YES; @@ -2547,8 +2510,6 @@ public: - (void)setMinimumZoomLevel:(double)minimumZoomLevel { - _mbglMap->setMinZoom(minimumZoomLevel); - [self validateTileCacheSize]; } - (double)minimumZoomLevel @@ -2559,7 +2520,6 @@ public: - (void)setMaximumZoomLevel:(double)maximumZoomLevel { _mbglMap->setMaxZoom(maximumZoomLevel); - [self validateTileCacheSize]; } - (double)maximumZoomLevel -- cgit v1.2.1 From 6aecc66fc876825e164675f745ca9c71000ce515 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 28 Jun 2017 13:05:12 -0400 Subject: [ios] Minimize tilt gesture delay --- platform/ios/src/MGLMapView.mm | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index ba37dc3d31..f3b0d8506a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -532,21 +532,21 @@ public: _singleTapGestureRecognizer.delegate = self; [self addGestureRecognizer:_singleTapGestureRecognizer]; - _twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTapGesture:)]; - _twoFingerTap.numberOfTouchesRequired = 2; - [_twoFingerTap requireGestureRecognizerToFail:_pinch]; - [_twoFingerTap requireGestureRecognizerToFail:_rotate]; - [self addGestureRecognizer:_twoFingerTap]; - _twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)]; _twoFingerDrag.minimumNumberOfTouches = 2; _twoFingerDrag.maximumNumberOfTouches = 2; _twoFingerDrag.delegate = self; - [_twoFingerDrag requireGestureRecognizerToFail:_twoFingerTap]; [_twoFingerDrag requireGestureRecognizerToFail:_pan]; [self addGestureRecognizer:_twoFingerDrag]; _pitchEnabled = YES; + _twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTapGesture:)]; + _twoFingerTap.numberOfTouchesRequired = 2; + [_twoFingerTap requireGestureRecognizerToFail:_pinch]; + [_twoFingerTap requireGestureRecognizerToFail:_rotate]; + [_twoFingerTap requireGestureRecognizerToFail:_twoFingerDrag]; + [self addGestureRecognizer:_twoFingerTap]; + _decelerationRate = MGLMapViewDecelerationRateNormal; _quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)]; @@ -1682,14 +1682,14 @@ public: if ( ! self.isPitchEnabled) return; _mbglMap->cancelTransitions(); - MGLMapCamera *oldCamera = self.camera; if (twoFingerDrag.state == UIGestureRecognizerStateBegan) { [self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag]; [self notifyGestureDidBegin]; } - else if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged) + + if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged) { CGFloat gestureDistance = CGPoint([twoFingerDrag translationInView:twoFingerDrag.view]).y; CGFloat currentPitch = _mbglMap->getPitch(); @@ -1699,6 +1699,7 @@ public: CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag]; + MGLMapCamera *oldCamera = self.camera; MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew]; if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || -- cgit v1.2.1 From 73869d12dd2e651b09a158a3cbd6100801b5c397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 5 Jan 2017 14:37:29 +0100 Subject: [build] enable -fvisibility=hidden for iOS --- platform/ios/src/MGLAnnotationImage.h | 3 +++ platform/ios/src/MGLAnnotationView.h | 3 +++ platform/ios/src/MGLMapView.h | 9 +++++---- platform/ios/src/MGLUserLocation.h | 2 ++ platform/ios/src/MGLUserLocationAnnotationView.h | 2 ++ 5 files changed, 15 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h index fbeee18624..0b5a111841 100644 --- a/platform/ios/src/MGLAnnotationImage.h +++ b/platform/ios/src/MGLAnnotationImage.h @@ -1,5 +1,7 @@ #import +#import "MGLFoundation.h" + NS_ASSUME_NONNULL_BEGIN /** @@ -8,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN objects and may be recycled later and put into a reuse queue that is maintained by the map view. */ +MGL_EXPORT @interface MGLAnnotationImage : NSObject #pragma mark Initializing and Preparing the Image Object diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 2802d31b05..4fa0f196ab 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -1,5 +1,7 @@ #import +#import "MGLFoundation.h" + NS_ASSUME_NONNULL_BEGIN @protocol MGLAnnotation; @@ -50,6 +52,7 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { interactivity such as dragging, you can use an `MGLAnnotationImage` instead to conserve memory and optimize drawing performance. */ +MGL_EXPORT @interface MGLAnnotationView : UIView #pragma mark Initializing and Preparing the View diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index ca765a046b..7a1d200e44 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -4,6 +4,7 @@ #import #import +#import "MGLFoundation.h" #import "MGLTypes.h" NS_ASSUME_NONNULL_BEGIN @@ -23,13 +24,13 @@ NS_ASSUME_NONNULL_BEGIN @protocol MGLFeature; /** The default deceleration rate for a map view. */ -extern const CGFloat MGLMapViewDecelerationRateNormal; +extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateNormal; /** A fast deceleration rate for a map view. */ -extern const CGFloat MGLMapViewDecelerationRateFast; +extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateFast; /** Disables deceleration in a map view. */ -extern const CGFloat MGLMapViewDecelerationRateImmediate; +extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateImmediate; /** The vertical alignment of an annotation within a map view. Used with @@ -125,7 +126,7 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) { ensuring that your use adheres to the relevant terms of use. */ -IB_DESIGNABLE +MGL_EXPORT IB_DESIGNABLE @interface MGLMapView : UIView #pragma mark Creating Instances diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h index 30bfc592ca..8c6fe46136 100644 --- a/platform/ios/src/MGLUserLocation.h +++ b/platform/ios/src/MGLUserLocation.h @@ -1,6 +1,7 @@ #import #import +#import "MGLFoundation.h" #import "MGLAnnotation.h" NS_ASSUME_NONNULL_BEGIN @@ -11,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN directly. Instead, you retrieve an existing `MGLUserLocation` object from the `userLocation` property of the map view displayed in your application. */ +MGL_EXPORT @interface MGLUserLocation : NSObject #pragma mark Determining the User’s Position diff --git a/platform/ios/src/MGLUserLocationAnnotationView.h b/platform/ios/src/MGLUserLocationAnnotationView.h index 4b36236b8d..4d95f39cf3 100644 --- a/platform/ios/src/MGLUserLocationAnnotationView.h +++ b/platform/ios/src/MGLUserLocationAnnotationView.h @@ -1,6 +1,7 @@ #import #import +#import "MGLFoundation.h" #import "MGLAnnotationView.h" NS_ASSUME_NONNULL_BEGIN @@ -9,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @class MGLUserLocation; /** View representing an `MGLUserLocation` on screen. */ +MGL_EXPORT @interface MGLUserLocationAnnotationView : MGLAnnotationView /** -- cgit v1.2.1 From 629922f5570abc46011db092d204c2e7642812c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 5 Jan 2017 14:37:29 +0100 Subject: [build] enable -fvisibility=hidden for iOS --- platform/ios/src/MGLAnnotationImage.h | 3 +++ platform/ios/src/MGLAnnotationView.h | 3 +++ platform/ios/src/MGLMapView.h | 9 +++++---- platform/ios/src/MGLUserLocation.h | 2 ++ platform/ios/src/MGLUserLocationAnnotationView.h | 2 ++ 5 files changed, 15 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h index fbeee18624..0b5a111841 100644 --- a/platform/ios/src/MGLAnnotationImage.h +++ b/platform/ios/src/MGLAnnotationImage.h @@ -1,5 +1,7 @@ #import +#import "MGLFoundation.h" + NS_ASSUME_NONNULL_BEGIN /** @@ -8,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN objects and may be recycled later and put into a reuse queue that is maintained by the map view. */ +MGL_EXPORT @interface MGLAnnotationImage : NSObject #pragma mark Initializing and Preparing the Image Object diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 2802d31b05..4fa0f196ab 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -1,5 +1,7 @@ #import +#import "MGLFoundation.h" + NS_ASSUME_NONNULL_BEGIN @protocol MGLAnnotation; @@ -50,6 +52,7 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { interactivity such as dragging, you can use an `MGLAnnotationImage` instead to conserve memory and optimize drawing performance. */ +MGL_EXPORT @interface MGLAnnotationView : UIView #pragma mark Initializing and Preparing the View diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 31320ac977..a93b8e8511 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -4,6 +4,7 @@ #import #import +#import "MGLFoundation.h" #import "MGLTypes.h" NS_ASSUME_NONNULL_BEGIN @@ -23,13 +24,13 @@ NS_ASSUME_NONNULL_BEGIN @protocol MGLFeature; /** The default deceleration rate for a map view. */ -extern const CGFloat MGLMapViewDecelerationRateNormal; +extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateNormal; /** A fast deceleration rate for a map view. */ -extern const CGFloat MGLMapViewDecelerationRateFast; +extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateFast; /** Disables deceleration in a map view. */ -extern const CGFloat MGLMapViewDecelerationRateImmediate; +extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateImmediate; /** The vertical alignment of an annotation within a map view. Used with @@ -125,7 +126,7 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) { ensuring that your use adheres to the relevant terms of use. */ -IB_DESIGNABLE +MGL_EXPORT IB_DESIGNABLE @interface MGLMapView : UIView #pragma mark Creating Instances diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h index 30bfc592ca..8c6fe46136 100644 --- a/platform/ios/src/MGLUserLocation.h +++ b/platform/ios/src/MGLUserLocation.h @@ -1,6 +1,7 @@ #import #import +#import "MGLFoundation.h" #import "MGLAnnotation.h" NS_ASSUME_NONNULL_BEGIN @@ -11,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN directly. Instead, you retrieve an existing `MGLUserLocation` object from the `userLocation` property of the map view displayed in your application. */ +MGL_EXPORT @interface MGLUserLocation : NSObject #pragma mark Determining the User’s Position diff --git a/platform/ios/src/MGLUserLocationAnnotationView.h b/platform/ios/src/MGLUserLocationAnnotationView.h index 4b36236b8d..4d95f39cf3 100644 --- a/platform/ios/src/MGLUserLocationAnnotationView.h +++ b/platform/ios/src/MGLUserLocationAnnotationView.h @@ -1,6 +1,7 @@ #import #import +#import "MGLFoundation.h" #import "MGLAnnotationView.h" NS_ASSUME_NONNULL_BEGIN @@ -9,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @class MGLUserLocation; /** View representing an `MGLUserLocation` on screen. */ +MGL_EXPORT @interface MGLUserLocationAnnotationView : MGLAnnotationView /** -- cgit v1.2.1 From 39a732d7ae3cb1b927d94c4b1154b42d9565356a Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 13 Jun 2017 10:50:16 +0300 Subject: [android][glfw][ios][macos][node][qt] split backend from mapobserver --- platform/ios/src/MGLMapView.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 1444fc3cb0..9789e4ac64 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -444,7 +444,7 @@ public: mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _mbglThreadPool = mbgl::sharedThreadPool(); - _mbglMap = new mbgl::Map(*_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + _mbglMap = new mbgl::Map(*_mbglView, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); // start paused if in IB if (_isTargetingInterfaceBuilder || background) { @@ -5408,7 +5408,7 @@ public: return _annotationViewReuseQueueByIdentifier[identifier]; } -class MBGLView : public mbgl::View, public mbgl::Backend +class MBGLView : public mbgl::View, public mbgl::Backend, public mbgl::MapObserver { public: MBGLView(MGLMapView* nativeView_) : nativeView(nativeView_) { -- cgit v1.2.1 From b6d56ad634e2b3048e97bedd9f674aa4ec975453 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Fri, 23 Jun 2017 16:05:08 -0700 Subject: [ios][macos] rendering interface changes --- platform/ios/src/MGLMapView.mm | 38 +++++++++++++++++++++-------------- platform/ios/src/MGLMapView_Private.h | 3 +++ 2 files changed, 26 insertions(+), 15 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9789e4ac64..133c704d0b 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #import "MGLMultiPoint_Private.h" #import "MGLOfflineStorage_Private.h" #import "MGLFoundation_Private.h" +#import "MGLRendererFrontend.h" #import "NSBundle+MGLAdditions.h" #import "NSDate+MGLAdditions.h" @@ -278,6 +280,8 @@ public: { mbgl::Map *_mbglMap; MBGLView *_mbglView; + std::unique_ptr _rendererFrontend; + std::shared_ptr _mbglThreadPool; BOOL _opaque; @@ -410,6 +414,11 @@ public: return _mbglMap; } +- (mbgl::Renderer *)renderer +{ + return _rendererFrontend->getRenderer(); +} + - (void)commonInit { _isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent; @@ -444,7 +453,10 @@ public: mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _mbglThreadPool = mbgl::sharedThreadPool(); - _mbglMap = new mbgl::Map(*_mbglView, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + + auto renderer = std::make_unique(*_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique); + _rendererFrontend = std::make_unique(std::move(renderer), self, _mbglView); + _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); // start paused if in IB if (_isTargetingInterfaceBuilder || background) { @@ -758,7 +770,7 @@ public: { MGLAssertIsMainThread(); - _mbglMap->onLowMemory(); + _rendererFrontend->onLowMemory(); } #pragma mark - Layout - @@ -810,12 +822,9 @@ public: // This is the delegate of the GLKView object's display call. - (void)glkView:(__unused GLKView *)view drawInRect:(__unused CGRect)rect { - if ( ! self.dormant) + if ( ! self.dormant || ! _rendererFrontend) { - // The OpenGL implementation automatically enables the OpenGL context for us. - mbgl::BackendScope scope { *_mbglView, mbgl::BackendScope::ScopeType::Implicit }; - - _mbglMap->render(*_mbglView); + _rendererFrontend->render(); [self updateUserLocationAnnotationView]; } @@ -2139,7 +2148,7 @@ public: - (void)emptyMemoryCache { - _mbglMap->onLowMemory(); + _rendererFrontend->onLowMemory(); } - (void)setZoomEnabled:(BOOL)zoomEnabled @@ -3707,7 +3716,7 @@ public: /// Returns the tags of the annotations coincident with the given rectangle. - (std::vector)annotationTagsInRect:(CGRect)rect { - return _mbglMap->queryPointAnnotations({ + return _rendererFrontend->getRenderer()->queryPointAnnotations({ { CGRectGetMinX(rect), CGRectGetMinY(rect) }, { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, }); @@ -4707,7 +4716,7 @@ public: optionalFilter = predicate.mgl_filter; } - std::vector features = _mbglMap->queryRenderedFeatures(screenCoordinate, { optionalLayerIDs, optionalFilter }); + std::vector features = _rendererFrontend->getRenderer()->queryRenderedFeatures(screenCoordinate, { optionalLayerIDs, optionalFilter }); return MGLFeaturesFromMBGLFeatures(features); } @@ -4740,7 +4749,7 @@ public: optionalFilter = predicate.mgl_filter; } - std::vector features = _mbglMap->queryRenderedFeatures(screenBox, { optionalLayerIDs, optionalFilter }); + std::vector features = _rendererFrontend->getRenderer()->queryRenderedFeatures(screenBox, { optionalLayerIDs, optionalFilter }); return MGLFeaturesFromMBGLFeatures(features); } @@ -5519,10 +5528,9 @@ public: return reinterpret_cast(symbol); } - - void invalidate() override - { - [nativeView setNeedsGLDisplay]; + + mbgl::BackendScope::ScopeType getScopeType() const override { + return mbgl::BackendScope::ScopeType::Implicit; } void activate() override diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h index 4e2765377c..482ab55c5e 100644 --- a/platform/ios/src/MGLMapView_Private.h +++ b/platform/ios/src/MGLMapView_Private.h @@ -2,6 +2,7 @@ namespace mbgl { class Map; + class Renderer; } /// Minimum size of an annotation’s accessibility element. @@ -17,6 +18,8 @@ extern const CGSize MGLAnnotationAccessibilityElementMinimumSize; - (mbgl::Map *)mbglMap; +- (mbgl::Renderer *)renderer; + /** Returns whether the map view is currently loading or processing any assets required to render the map */ - (BOOL)isFullyLoaded; -- cgit v1.2.1 From 815ffb253b555c9a99b7d63a343745db05802c76 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 5 Jul 2017 20:20:20 +0300 Subject: [android][glfw][ios][macos][node][qt] rename backend to renderer backend --- platform/ios/src/MGLMapView.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 133c704d0b..1800332e42 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -15,13 +15,13 @@ #include #include #include -#include #include #include #include #include -#include -#include +#include +#include +#include #include #include #include @@ -5417,7 +5417,7 @@ public: return _annotationViewReuseQueueByIdentifier[identifier]; } -class MBGLView : public mbgl::View, public mbgl::Backend, public mbgl::MapObserver +class MBGLView : public mbgl::View, public mbgl::RendererBackend, public mbgl::MapObserver { public: MBGLView(MGLMapView* nativeView_) : nativeView(nativeView_) { -- cgit v1.2.1 From dcd9a38388fe5fc4c70a3e39af4db10a3c09ed49 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 24 Jul 2017 19:24:24 -0400 Subject: [ios] Don't force the attribution button tint color to update --- platform/ios/src/MGLMapView.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index f3b0d8506a..2de1496998 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1144,8 +1144,10 @@ public: - (void)updateTintColorForView:(UIView *)view { - // stop at recursing container & annotation views (#8522) - if ([view isEqual:self.annotationContainerView]) return; + // Don't update: + // - annotation views + // - attribution button (handled automatically) + if ([view isEqual:self.annotationContainerView] || [view isEqual:self.attributionButton]) return; if ([view respondsToSelector:@selector(setTintColor:)]) view.tintColor = self.tintColor; -- cgit v1.2.1 From 66e1429460b0aad6b295773db6e6fe965831ff3c Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 25 Jul 2017 10:56:00 +0300 Subject: [ios][macos] manage backendscope in renderer frontend --- platform/ios/src/MGLMapView.mm | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 1800332e42..83ea4a8d8f 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -455,7 +455,7 @@ public: _mbglThreadPool = mbgl::sharedThreadPool(); auto renderer = std::make_unique(*_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique); - _rendererFrontend = std::make_unique(std::move(renderer), self, _mbglView); + _rendererFrontend = std::make_unique(std::move(renderer), self, *_mbglView, *_mbglView); _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); // start paused if in IB @@ -5528,10 +5528,6 @@ public: return reinterpret_cast(symbol); } - - mbgl::BackendScope::ScopeType getScopeType() const override { - return mbgl::BackendScope::ScopeType::Implicit; - } void activate() override { -- cgit v1.2.1 From d4cb498d7abc612029c575fb290eb649a4697d57 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 21 Jul 2017 16:36:31 -0700 Subject: [all] Merge View into RendererBackend --- platform/ios/src/MGLMapView.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 83ea4a8d8f..175e1f125d 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -6,7 +6,6 @@ #import #include -#include #include #include #include @@ -455,7 +454,7 @@ public: _mbglThreadPool = mbgl::sharedThreadPool(); auto renderer = std::make_unique(*_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique); - _rendererFrontend = std::make_unique(std::move(renderer), self, *_mbglView, *_mbglView); + _rendererFrontend = std::make_unique(std::move(renderer), self, *_mbglView); _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); // start paused if in IB @@ -5417,7 +5416,7 @@ public: return _annotationViewReuseQueueByIdentifier[identifier]; } -class MBGLView : public mbgl::View, public mbgl::RendererBackend, public mbgl::MapObserver +class MBGLView : public mbgl::RendererBackend, public mbgl::MapObserver { public: MBGLView(MGLMapView* nativeView_) : nativeView(nativeView_) { -- cgit v1.2.1 From 0cdef85798f7d2f78a732bb637f378560c1dce58 Mon Sep 17 00:00:00 2001 From: danielamitaysc Date: Mon, 24 Jul 2017 15:32:39 -0400 Subject: [ios] Proper limiting behavior when trying to zoom out fully - Bail from quick zoom if new zoom is same as old zoom - Allow pinch gesture at minZoom to still move the map --- platform/ios/src/MGLMapView.mm | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 2de1496998..3db0bc03b2 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1280,8 +1280,6 @@ public: { if ( ! self.isZoomEnabled) return; - if (_mbglMap->getZoom() <= _mbglMap->getMinZoom() && pinch.scale < 1) return; - _mbglMap->cancelTransitions(); CGPoint centerPoint = [self anchorPointForGesture:pinch]; @@ -1297,17 +1295,17 @@ public: } else if (pinch.state == UIGestureRecognizerStateChanged) { + // Zoom limiting happens at the core level. CGFloat newScale = self.scale * pinch.scale; - double zoom = log2(newScale); - if (zoom < _mbglMap->getMinZoom()) return; - + double newZoom = log2(newScale); + // Calculates the final camera zoom, has no effect within current map camera. - MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint]; + MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint]; if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] || [self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera]) { - _mbglMap->setZoom(zoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); + _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); // The gesture recognizer only reports the gesture’s current center // point, so use the previous center point to anchor the transition. // If the number of touches has changed, the remembered center point is @@ -1655,9 +1653,9 @@ public: { CGFloat distance = [quickZoom locationInView:quickZoom.view].y - self.quickZoomStart; - CGFloat newZoom = log2f(self.scale) + (distance / 75); + CGFloat newZoom = MAX(log2f(self.scale) + (distance / 75), _mbglMap->getMinZoom()); - if (newZoom < _mbglMap->getMinZoom()) return; + if (_mbglMap->getZoom() == newZoom) return; CGPoint centerPoint = [self anchorPointForGesture:quickZoom]; -- cgit v1.2.1 From 6d629597d7e05684ece16ae4239f5ab06ec386e2 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Thu, 27 Jul 2017 15:16:39 -0700 Subject: [ios] Make tilt gesture recognizer recognizable only with two horizontal fingers (#9571) * [ios] Make tilt gesture recognizer recognizable only with two horizontal fingers * [ios] Make tilt gesture recognizer tolerance degree less restrictive --- platform/ios/src/MGLMapView.mm | 81 +++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 33 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 3db0bc03b2..8dbf26da55 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1788,39 +1788,6 @@ public: return [gesture locationInView:gesture.view]; } -- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer -{ - if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) - { - UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer; - - if (panGesture.minimumNumberOfTouches == 2) - { - CGPoint velocity = [panGesture velocityInView:panGesture.view]; - double gestureAngle = MGLDegreesFromRadians(atan(velocity.y / velocity.x)); - double horizontalToleranceDegrees = 20.0; - - // cancel if gesture angle is not 90º±20º (more or less vertical) - if ( ! (fabs((fabs(gestureAngle) - 90.0)) < horizontalToleranceDegrees)) - { - return NO; - } - } - } - else if (gestureRecognizer == _singleTapGestureRecognizer) - { - // Gesture will be recognized if it could deselect an annotation - if(!self.selectedAnnotation) - { - idannotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO]; - if(!annotation) { - return NO; - } - } - } - return YES; -} - - (void)handleCalloutAccessoryTapGesture:(UITapGestureRecognizer *)tap { if ([self.delegate respondsToSelector:@selector(mapView:annotation:calloutAccessoryControlTapped:)]) @@ -1864,12 +1831,60 @@ public: UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, calloutView); } +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) + { + UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer; + + if (panGesture.minimumNumberOfTouches == 2) + { + CGPoint west = [panGesture locationOfTouch:0 inView:panGesture.view]; + CGPoint east = [panGesture locationOfTouch:1 inView:panGesture.view]; + + if (west.x > east.x) { + CGPoint swap = west; + west = east; + east = swap; + } + + float horizontalToleranceDegrees = 60.0; + if ([self angleBetweenPoints:west east:east] > horizontalToleranceDegrees) { + return NO; + } + + } + } + else if (gestureRecognizer == _singleTapGestureRecognizer) + { + // Gesture will be recognized if it could deselect an annotation + if(!self.selectedAnnotation) + { + idannotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO]; + if(!annotation) { + return NO; + } + } + } + return YES; +} + - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ]; return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]); } + +- (float)angleBetweenPoints:(CGPoint)west east:(CGPoint)east +{ + float slope = (west.y - east.y) / (west.x - east.x); + + float angle = atan(fabs(slope)); + float degrees = MGLDegreesFromRadians(angle); + + return degrees; +} - (void)trackGestureEvent:(NSString *)gestureID forRecognizer:(UIGestureRecognizer *)recognizer { -- cgit v1.2.1 From b0bae6d1dde4e8c3b0b0b50d4255e1073f36022a Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 28 Jul 2017 10:22:16 -0700 Subject: [ios] tilt gesture type fix (#9642) * [ios] Change function:angleBetweenPoints return type * [ios] Update changelog --- platform/ios/src/MGLMapView.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 8dbf26da55..9cc4c42e5a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1848,7 +1848,7 @@ public: east = swap; } - float horizontalToleranceDegrees = 60.0; + CLLocationDegrees horizontalToleranceDegrees = 60.0; if ([self angleBetweenPoints:west east:east] > horizontalToleranceDegrees) { return NO; } @@ -1876,12 +1876,12 @@ public: return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]); } -- (float)angleBetweenPoints:(CGPoint)west east:(CGPoint)east +- (CLLocationDegrees)angleBetweenPoints:(CGPoint)west east:(CGPoint)east { - float slope = (west.y - east.y) / (west.x - east.x); + CGFloat slope = (west.y - east.y) / (west.x - east.x); - float angle = atan(fabs(slope)); - float degrees = MGLDegreesFromRadians(angle); + CGFloat angle = atan(fabs(slope)); + CLLocationDegrees degrees = MGLDegreesFromRadians(angle); return degrees; } -- cgit v1.2.1 From 6a8ed6f83ae6ae9494cfc075214ba4558abd7e85 Mon Sep 17 00:00:00 2001 From: Fredrik Karlsson Date: Mon, 31 Jul 2017 13:57:41 +0200 Subject: [ios] expose setCamera with edge padding --- platform/ios/src/MGLMapView.h | 17 +++++++++++++++++ platform/ios/src/MGLMapView.mm | 6 +++++- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index a93b8e8511..a0aada9a9c 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -764,6 +764,23 @@ MGL_EXPORT IB_DESIGNABLE */ - (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion; +/** + Moves the viewpoint to a different location with respect to the map with an + optional transition duration and timing function. + + @param camera The new viewpoint. + @param duration The amount of time, measured in seconds, that the transition + animation should take. Specify `0` to jump to the new viewpoint + instantaneously. + @param function A timing function used for the animation. Set this parameter to + `nil` for a transition that matches most system animations. If the duration + is `0`, this parameter is ignored. + @param edgePadding The minimum padding (in screen points) that would be visible + around the returned camera object if it were set as the receiver’s camera. + @param completion The block to execute after the animation finishes. + */ +- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion; + /** Moves the viewpoint to a different location using a transition animation that evokes powered flight and a default duration based on the length of the flight diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9cc4c42e5a..715c32186d 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2788,6 +2788,10 @@ public: - (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion { + [self setCamera:camera withDuration:duration animationTimingFunction:function edgePadding:self.contentInset completionHandler:completion]; +} + +- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion { mbgl::AnimationOptions animationOptions; if (duration > 0) { @@ -2816,7 +2820,7 @@ public: [self willChangeValueForKey:@"camera"]; _mbglMap->cancelTransitions(); - mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:self.contentInset]; + mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding]; _mbglMap->easeTo(cameraOptions, animationOptions); [self didChangeValueForKey:@"camera"]; } -- cgit v1.2.1 From 1394bfb554c5daa5d2384d960c9118e880a30480 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 7 Aug 2017 14:33:39 -0400 Subject: [ios] Fix user location horizontal accuracy ring inaccuracy Accuracy was off by +25%, depending on latitude. --- platform/ios/src/MGLFaux3DUserLocationAnnotationView.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 6db9c0db10..5f67f24f4e 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -460,11 +460,8 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck - (CGFloat)calculateAccuracyRingSize { - CGFloat latitudeRadians = MGLRadiansFromDegrees(self.userLocation.coordinate.latitude); - CGFloat metersPerPoint = [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude]; - CGFloat pixelRadius = self.userLocation.location.horizontalAccuracy / cos(latitudeRadians) / metersPerPoint; - - return pixelRadius * 2.0; + // diameter in screen points + return self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0; } - (UIImage *)headingIndicatorTintedGradientImage -- cgit v1.2.1 From 6179e0f9c05d178b2475e54a49568b57d66b68ae Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Fri, 11 Aug 2017 14:38:19 +0300 Subject: [core] Added Style::getDefaultCamera() --- platform/ios/src/MGLMapView.mm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 175e1f125d..9c3d1d415f 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2136,10 +2136,11 @@ public: - (void)resetPosition { - CGFloat pitch = _mbglMap->getStyle().getDefaultPitch(); - CLLocationDirection heading = mbgl::util::wrap(_mbglMap->getStyle().getDefaultBearing(), 0., 360.); - CLLocationDistance distance = MGLAltitudeForZoomLevel(_mbglMap->getStyle().getDefaultZoom(), pitch, 0, self.frame.size); - self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(_mbglMap->getStyle().getDefaultLatLng()) + auto camera = _mbglMap->getStyle().getDefaultCamera(); + CGFloat pitch = *camera.pitch; + CLLocationDirection heading = mbgl::util::wrap(*camera.angle, 0., 360.); + CLLocationDistance distance = MGLAltitudeForZoomLevel(*camera.zoom, pitch, 0, self.frame.size); + self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(*camera.center) fromDistance:distance pitch:pitch heading:heading]; -- cgit v1.2.1 From acb8199d326eda02102b2d409ebec510053fec1b Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 18 Aug 2017 15:17:04 -0400 Subject: [ios] Document annotations-as-features limitations w/feature querying Also move visibleAnnotations next to visibleAnnotationsInRect:. --- platform/ios/src/MGLMapView.h | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index a0aada9a9c..a514d15b41 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -990,16 +990,6 @@ MGL_EXPORT IB_DESIGNABLE */ @property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *annotations; -/** - The complete list of annotations associated with the receiver that are - currently visible. - - The objects in this array must adopt the `MGLAnnotation` protocol. If no - annotations are associated with the map view or if no annotations associated - with the map view are currently visible, the value of this property is `nil`. - */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *visibleAnnotations; - /** Adds an annotation to the map view. @@ -1094,6 +1084,16 @@ MGL_EXPORT IB_DESIGNABLE */ - (nullable __kindof MGLAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier; +/** + The complete list of annotations associated with the receiver that are + currently visible. + + The objects in this array must adopt the `MGLAnnotation` protocol. If no + annotations are associated with the map view or if no annotations associated + with the map view are currently visible, the value of this property is `nil`. + */ +@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *visibleAnnotations; + /** Returns the list of annotations associated with the receiver that intersect with the given rectangle. @@ -1254,6 +1254,11 @@ MGL_EXPORT IB_DESIGNABLE `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and `-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources. + The returned features may also include features corresponding to annotations. + These features are not object-equal to the `MGLAnnotation` objects that were + originally added to the map. To query the map for annotations, use + `visibleAnnotations` or `-[MGLMapView visibleAnnotationsInRect:]`. + @note Layer identifiers are not guaranteed to exist across styles or different versions of the same style. Applications that use this API must first set the style URL to an explicitly versioned style using a convenience method -- cgit v1.2.1 From f2c9f2bf3695fc30579a40978ebda9d87e4b4bcd Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Tue, 22 Aug 2017 11:41:37 -0700 Subject: [ios][macos][node][android] Updated documentation and Changelogs for #9821 --- platform/ios/src/MGLMapView.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 7a1d200e44..64d2e53d21 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -597,7 +597,8 @@ MGL_EXPORT IB_DESIGNABLE * If the value of this property is smaller than that of the * minimumZoomLevel property, the behavior is undefined. * - * The default maximumZoomLevel is 20. + * The default maximumZoomLevel is 22. The upper bound for this property + * is 25.5. */ @property (nonatomic) double maximumZoomLevel; -- cgit v1.2.1 From b2fd7b680c0bdcb5918424487aba9f083149c123 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 23 Aug 2017 16:06:12 -0400 Subject: [ios] Fix heading update loop Updating CLLocationManager.headingOrientation triggers a new heading update, even if there was no actual change in its value. --- platform/ios/src/MGLMapView.mm | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 715c32186d..18a8d2608a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4690,30 +4690,39 @@ public: { // note that right/left device and interface orientations are opposites (see UIApplication.h) // + CLDeviceOrientation orientation; switch ([[UIApplication sharedApplication] statusBarOrientation]) { case (UIInterfaceOrientationLandscapeLeft): { - self.locationManager.headingOrientation = CLDeviceOrientationLandscapeRight; + orientation = CLDeviceOrientationLandscapeRight; break; } case (UIInterfaceOrientationLandscapeRight): { - self.locationManager.headingOrientation = CLDeviceOrientationLandscapeLeft; + orientation = CLDeviceOrientationLandscapeLeft; break; } case (UIInterfaceOrientationPortraitUpsideDown): { - self.locationManager.headingOrientation = CLDeviceOrientationPortraitUpsideDown; + orientation = CLDeviceOrientationPortraitUpsideDown; break; } case (UIInterfaceOrientationPortrait): default: { - self.locationManager.headingOrientation = CLDeviceOrientationPortrait; + orientation = CLDeviceOrientationPortrait; break; } } + + // Setting the location manager's heading orientation causes it to send + // a heading event, which in turn makes us redraw, which kicks off a + // loop... so don't do that. rdar://34059173 + if (self.locationManager.headingOrientation != orientation) + { + self.locationManager.headingOrientation = orientation; + } } } -- cgit v1.2.1 From a7f06366d2c431bdf34fcfa14314be43ac09987b Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 25 Aug 2017 18:06:39 -0400 Subject: [ios] Support iOS 11 location usage descriptions --- platform/ios/src/MGLMapView.mm | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 18a8d2608a..af7122fc14 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4160,23 +4160,35 @@ public: { self.locationManager = [[CLLocationManager alloc] init]; - if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)] && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) + if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) { - BOOL hasLocationDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] || [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]; - if (!hasLocationDescription) + BOOL requiresWhenInUseUsageDescription = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){11,0,0}]; + BOOL hasWhenInUseUsageDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]; + BOOL hasAlwaysUsageDescription; + if (requiresWhenInUseUsageDescription) { - [NSException raise:@"Missing Location Services usage description" format: - @"This app must have a value for NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription in its Info.plist."]; + hasAlwaysUsageDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] && hasWhenInUseUsageDescription; + } + else + { + hasAlwaysUsageDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]; } - if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]) + if (hasAlwaysUsageDescription) { [self.locationManager requestAlwaysAuthorization]; } - else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) + else if (hasWhenInUseUsageDescription) { [self.locationManager requestWhenInUseAuthorization]; } + else + { + NSString *suggestedUsageKeys = requiresWhenInUseUsageDescription ? + @"NSLocationWhenInUseUsageDescription and (optionally) NSLocationAlwaysAndWhenInUseUsageDescription" : + @"NSLocationWhenInUseUsageDescription and/or NSLocationAlwaysUsageDescription"; + [NSException raise:@"Missing Location Services usage description" format:@"This app must have a value for %@ in its Info.plist.", suggestedUsageKeys]; + } } self.locationManager.headingFilter = 5.0; -- cgit v1.2.1 From e78620c70567a995a6f02ae3ee85cc5dd7f3fade Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Thu, 27 Jul 2017 22:28:41 -0400 Subject: [ios] Set location to nil until the user's location is determined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation for the `location` property states that "This property contains `nil` if the map view is not currently showing the user location or if the user’s location has not yet been determined." The iOS SDK presently returns a garbage value, which has some rather annoying consequences when the value should logically be nullable. This change should rectify the issue. With _location no longer initialized to an invalid coordinate, trying to access `_location.coordinate` when `_location == nil` will return `0, 0`, which is a valid coordinate. --- platform/ios/src/MGLUserLocation.h | 3 +-- platform/ios/src/MGLUserLocation.m | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h index 8c6fe46136..91abadbcb7 100644 --- a/platform/ios/src/MGLUserLocation.h +++ b/platform/ios/src/MGLUserLocation.h @@ -20,8 +20,7 @@ MGL_EXPORT /** The current location of the device. (read-only) - This property contains `nil` if the map view is not currently showing the user - location or if the user’s location has not yet been determined. + This property returns `nil` if the user’s location has not yet been determined. */ @property (nonatomic, readonly, nullable) CLLocation *location; diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m index 1c9649c09e..074d138a72 100644 --- a/platform/ios/src/MGLUserLocation.m +++ b/platform/ios/src/MGLUserLocation.m @@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_END { if (self = [super init]) { - _location = [[CLLocation alloc] initWithLatitude:MAXFLOAT longitude:MAXFLOAT]; _mapView = mapView; } @@ -102,7 +101,7 @@ NS_ASSUME_NONNULL_END - (CLLocationCoordinate2D)coordinate { - return self.location.coordinate; + return _location ? _location.coordinate : kCLLocationCoordinate2DInvalid; } - (NSString *)title -- cgit v1.2.1 From c8dc77f06fa58112797bf1ae21fe8578512dc317 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Wed, 30 Aug 2017 16:25:57 -0400 Subject: [ios, macos] Make minimumZoomLevel and maximumZoomLevel IBInspectable. (#9729) * [ios, macos] Make minimumZoomLevel and maximumZoomLevel IBInspectable. * [ios, macos] Update changelogs. --- platform/ios/src/MGLMapView.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index a514d15b41..b475a21d2b 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -577,7 +577,7 @@ MGL_EXPORT IB_DESIGNABLE * * The default minimumZoomLevel is 0. */ -@property (nonatomic) double minimumZoomLevel; +@property (nonatomic) IBInspectable double minimumZoomLevel; /** * The maximum zoom level the map can be shown at. @@ -587,7 +587,7 @@ MGL_EXPORT IB_DESIGNABLE * * The default maximumZoomLevel is 20. */ -@property (nonatomic) double maximumZoomLevel; +@property (nonatomic) IBInspectable double maximumZoomLevel; /** The heading of the map, measured in degrees clockwise from true north. -- cgit v1.2.1 From 87348234d16915176fab313f0d3f2dae246216d9 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 30 Aug 2017 12:34:21 -0400 Subject: [ios] Guard against nil user location when setting tracking mode --- platform/ios/src/MGLMapView.mm | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index af7122fc14..320c4bcda8 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4338,12 +4338,6 @@ public: [self.locationManager stopUpdatingHeading]; - CLLocation *location = self.userLocation.location; - if (location && self.userLocationAnnotationView) - { - [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated]; - } - break; } case MGLUserTrackingModeFollowWithHeading: @@ -4360,11 +4354,6 @@ public: [self setZoomLevel:self.currentMinimumZoom animated:YES]; } - if (self.userLocationAnnotationView) - { - [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated]; - } - [self updateHeadingForDeviceOrientation]; [self.locationManager startUpdatingHeading]; @@ -4373,6 +4362,15 @@ public: } } + if (_userTrackingMode != MGLUserTrackingModeNone) + { + CLLocation *location = self.userLocation.location; + if (location && self.userLocationAnnotationView) + { + [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated]; + } + } + if ([self.delegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)]) { [self.delegate mapView:self didChangeUserTrackingMode:_userTrackingMode animated:animated]; @@ -4411,9 +4409,11 @@ public: if (self.userTrackingMode == MGLUserTrackingModeFollowWithCourse) { self.userTrackingState = MGLUserTrackingStatePossible; - if (self.userLocation.location) + + CLLocation *location = self.userLocation.location; + if (location) { - [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated]; + [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated]; } } } -- cgit v1.2.1 From 6d474717673530d6e4160fa251bbf226d301f1f1 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 30 Aug 2017 17:22:28 +0300 Subject: [ios] extend ui image additions with init from premultiplied image --- platform/ios/src/UIImage+MGLAdditions.h | 2 ++ platform/ios/src/UIImage+MGLAdditions.mm | 13 +++++++++++++ 2 files changed, 15 insertions(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h index 6e15e07cb5..3c179d6324 100644 --- a/platform/ios/src/UIImage+MGLAdditions.h +++ b/platform/ios/src/UIImage+MGLAdditions.h @@ -8,6 +8,8 @@ NS_ASSUME_NONNULL_BEGIN - (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage; +- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage; + - (std::unique_ptr)mgl_styleImageWithIdentifier:(NSString *)identifier; - (mbgl::PremultipliedImage)mgl_premultipliedImage; diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm index 5e28d18190..7cf1ed9bcc 100644 --- a/platform/ios/src/UIImage+MGLAdditions.mm +++ b/platform/ios/src/UIImage+MGLAdditions.mm @@ -22,6 +22,19 @@ return self; } +- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage +{ + CGImageRef image = CGImageFromMGLPremultipliedImage(mbglImage.clone()); + if (!image) { + return nil; + } + + self = [self initWithCGImage:image scale:1.0 orientation:UIImageOrientationUp]; + + CGImageRelease(image); + return self; +} + - (std::unique_ptr)mgl_styleImageWithIdentifier:(NSString *)identifier { BOOL isTemplate = self.renderingMode == UIImageRenderingModeAlwaysTemplate; return std::make_unique([identifier UTF8String], -- cgit v1.2.1 From b3b9ef5c6c090a5c8ec01ab041c756de54deb289 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 30 Aug 2017 17:22:48 +0300 Subject: [ios] snapshotter --- platform/ios/src/Mapbox.h | 1 + 1 file changed, 1 insertion(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index abe16cc3ee..9b2c472cf6 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -60,3 +60,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "NSValue+MGLAdditions.h" #import "MGLStyleValue.h" #import "MGLAttributionInfo.h" +#import "MGLMapSnapshotter.h" -- cgit v1.2.1 From c6708442c98665df36eabf3b0b7ca7ba38dfbdb4 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 9 Aug 2017 15:43:33 -0400 Subject: [ios] Refactor user location heading indicator into its own class --- .../ios/src/MGLFaux3DUserLocationAnnotationView.h | 7 +- .../ios/src/MGLFaux3DUserLocationAnnotationView.m | 133 +++------------------ platform/ios/src/MGLUserLocationHeadingBeamLayer.h | 10 ++ platform/ios/src/MGLUserLocationHeadingBeamLayer.m | 104 ++++++++++++++++ 4 files changed, 136 insertions(+), 118 deletions(-) create mode 100644 platform/ios/src/MGLUserLocationHeadingBeamLayer.h create mode 100644 platform/ios/src/MGLUserLocationHeadingBeamLayer.m (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h index c48dd6b27b..d5dae3a919 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h @@ -1,7 +1,12 @@ #import #import "MGLUserLocationAnnotationView.h" +const CGFloat MGLUserLocationAnnotationDotSize = 22.0; +const CGFloat MGLUserLocationAnnotationHaloSize = 115.0; + +const CGFloat MGLUserLocationAnnotationPuckSize = 45.0; +const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6; + @interface MGLFaux3DUserLocationAnnotationView : MGLUserLocationAnnotationView @end - diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 5f67f24f4e..36c5292127 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -2,14 +2,7 @@ #import "MGLMapView.h" #import "MGLUserLocation.h" - -const CGFloat MGLUserLocationAnnotationDotSize = 22.0; -const CGFloat MGLUserLocationAnnotationHaloSize = 115.0; - -const CGFloat MGLUserLocationAnnotationPuckSize = 45.0; -const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6; - -#pragma mark - +#import "MGLUserLocationHeadingBeamLayer.h" @implementation MGLFaux3DUserLocationAnnotationView { @@ -18,8 +11,7 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck CALayer *_puckDot; CAShapeLayer *_puckArrow; - CALayer *_headingIndicatorLayer; - CAShapeLayer *_headingIndicatorMaskLayer; + MGLUserLocationHeadingBeamLayer *_headingIndicatorLayer; CALayer *_accuracyRingLayer; CALayer *_dotBorderLayer; CALayer *_dotLayer; @@ -56,21 +48,18 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck - (void)setTintColor:(UIColor *)tintColor { + CGColorRef newTintColor = [tintColor CGColor]; + if (_puckModeActivated) { - _puckArrow.fillColor = [tintColor CGColor]; + _puckArrow.fillColor = newTintColor; } else { - if (_accuracyRingLayer) - { - _accuracyRingLayer.backgroundColor = [tintColor CGColor]; - } - - _haloLayer.backgroundColor = [tintColor CGColor]; - _dotLayer.backgroundColor = [tintColor CGColor]; - - _headingIndicatorLayer.contents = (__bridge id)[[self headingIndicatorTintedGradientImage] CGImage]; + _accuracyRingLayer.backgroundColor = newTintColor; + _haloLayer.backgroundColor = newTintColor; + _dotLayer.backgroundColor = newTintColor; + [_headingIndicatorLayer updateTintColor:newTintColor]; } } @@ -138,7 +127,6 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck self.layer.sublayers = nil; _headingIndicatorLayer = nil; - _headingIndicatorMaskLayer = nil; _accuracyRingLayer = nil; _haloLayer = nil; _dotBorderLayer = nil; @@ -232,47 +220,21 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck if (showHeadingIndicator) { _headingIndicatorLayer.hidden = NO; + CLLocationDirection headingAccuracy = self.userLocation.heading.headingAccuracy; // heading indicator (tinted, semi-circle) // - if ( ! _headingIndicatorLayer && self.userLocation.heading.headingAccuracy) + if ( ! _headingIndicatorLayer && headingAccuracy) { - CGFloat headingIndicatorSize = MGLUserLocationAnnotationHaloSize; - - _headingIndicatorLayer = [CALayer layer]; - _headingIndicatorLayer.bounds = CGRectMake(0, 0, headingIndicatorSize, headingIndicatorSize); - _headingIndicatorLayer.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0); - _headingIndicatorLayer.contents = (__bridge id)[[self headingIndicatorTintedGradientImage] CGImage]; - _headingIndicatorLayer.contentsGravity = kCAGravityBottom; - _headingIndicatorLayer.contentsScale = [UIScreen mainScreen].scale; - _headingIndicatorLayer.opacity = 0.4; - _headingIndicatorLayer.shouldRasterize = YES; - _headingIndicatorLayer.rasterizationScale = [UIScreen mainScreen].scale; - _headingIndicatorLayer.drawsAsynchronously = YES; - + _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self]; [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer]; - } - - // heading indicator accuracy mask (fan-shaped) - // - if ( ! _headingIndicatorMaskLayer && self.userLocation.heading.headingAccuracy) - { - _headingIndicatorMaskLayer = [CAShapeLayer layer]; - _headingIndicatorMaskLayer.frame = _headingIndicatorLayer.bounds; - _headingIndicatorMaskLayer.path = [[self headingIndicatorClippingMask] CGPath]; - - // apply the mask to the halo-radius-sized gradient layer - _headingIndicatorLayer.mask = _headingIndicatorMaskLayer; - - _oldHeadingAccuracy = self.userLocation.heading.headingAccuracy; + _oldHeadingAccuracy = headingAccuracy; } - else if (_oldHeadingAccuracy != self.userLocation.heading.headingAccuracy) + else if (_oldHeadingAccuracy != headingAccuracy) { - // recalculate the clipping mask based on updated accuracy - _headingIndicatorMaskLayer.path = [[self headingIndicatorClippingMask] CGPath]; - - _oldHeadingAccuracy = self.userLocation.heading.headingAccuracy; + [_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy]; + _oldHeadingAccuracy = headingAccuracy; } if (self.userLocation.heading.trueHeading >= 0) @@ -283,9 +245,7 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck else { [_headingIndicatorLayer removeFromSuperlayer]; - [_headingIndicatorMaskLayer removeFromSuperlayer]; _headingIndicatorLayer = nil; - _headingIndicatorMaskLayer = nil; } @@ -464,65 +424,4 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck return self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0; } -- (UIImage *)headingIndicatorTintedGradientImage -{ - UIImage *image; - - CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0; - - UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0); - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = UIGraphicsGetCurrentContext(); - - // gradient from the tint color to no-alpha tint color - CGFloat gradientLocations[] = {0.0, 1.0}; - CGGradientRef gradient = CGGradientCreateWithColors( - colorSpace, - (__bridge CFArrayRef)@[ - (id)[self.mapView.tintColor CGColor], - (id)[[self.mapView.tintColor colorWithAlphaComponent:0] CGColor]], - gradientLocations); - - // draw the gradient from the center point to the edge (full halo radius) - CGPoint centerPoint = CGPointMake(haloRadius, haloRadius); - CGContextDrawRadialGradient(context, gradient, - centerPoint, 0.0, - centerPoint, haloRadius, - kNilOptions); - - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - CGGradientRelease(gradient); - CGColorSpaceRelease(colorSpace); - - return image; -} - -- (UIBezierPath *)headingIndicatorClippingMask -{ - CGFloat accuracy = self.userLocation.heading.headingAccuracy; - - // size the mask using accuracy, but keep within a good display range - CGFloat clippingDegrees = 90 - accuracy; - clippingDegrees = fmin(clippingDegrees, 70); // most accurate - clippingDegrees = fmax(clippingDegrees, 10); // least accurate - - CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize); - UIBezierPath *ovalPath = UIBezierPath.bezierPath; - - // clip the oval to ± incoming accuracy degrees (converted to radians), from the top - [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)) - radius:CGRectGetWidth(ovalRect) / 2.0 - startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees) - endAngle:MGLRadiansFromDegrees(-clippingDegrees) - clockwise:YES]; - - [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))]; - [ovalPath closePath]; - - return ovalPath; -} - @end diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.h b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h new file mode 100644 index 0000000000..9a5d4a8f14 --- /dev/null +++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h @@ -0,0 +1,10 @@ +#import +#import "MGLUserLocationAnnotationView.h" + +@interface MGLUserLocationHeadingBeamLayer : CALayer + +- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView; +- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy; +- (void)updateTintColor:(CGColorRef)color; + +@end diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.m b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m new file mode 100644 index 0000000000..efe7e4db93 --- /dev/null +++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m @@ -0,0 +1,104 @@ +#import "MGLUserLocationHeadingBeamLayer.h" + +#import "MGLFaux3DUserLocationAnnotationView.h" +#import "MGLGeometry.h" + +@implementation MGLUserLocationHeadingBeamLayer +{ + CAShapeLayer *_maskLayer; +} + +- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView +{ + CGFloat size = MGLUserLocationAnnotationHaloSize; + + self = [super init]; + self.bounds = CGRectMake(0, 0, size, size); + self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds)); + self.contents = (__bridge id)[self gradientImageWithTintColor:userLocationView.tintColor.CGColor]; + self.contentsGravity = kCAGravityBottom; + self.contentsScale = UIScreen.mainScreen.scale; + self.opacity = 0.4; + self.shouldRasterize = YES; + self.rasterizationScale = UIScreen.mainScreen.scale; + self.drawsAsynchronously = YES; + + _maskLayer = [CAShapeLayer layer]; + _maskLayer.frame = self.bounds; + _maskLayer.path = [self clippingMaskForAccuracy:0]; + self.mask = _maskLayer; + + return self; +} + +- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy +{ + // recalculate the clipping mask based on updated accuracy + _maskLayer.path = [self clippingMaskForAccuracy:accuracy]; +} + +- (void)updateTintColor:(CGColorRef)color +{ + // redraw the raw tinted gradient + self.contents = (__bridge id)[self gradientImageWithTintColor:color]; +} + +- (CGImageRef)gradientImageWithTintColor:(CGColorRef)tintColor +{ + UIImage *image; + + CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = UIGraphicsGetCurrentContext(); + + // gradient from the tint color to no-alpha tint color + CGFloat gradientLocations[] = {0.0, 1.0}; + CGGradientRef gradient = CGGradientCreateWithColors( + colorSpace, + (__bridge CFArrayRef)@[(__bridge id)tintColor, + (id)CFBridgingRelease(CGColorCreateCopyWithAlpha(tintColor, 0))], + gradientLocations); + + // draw the gradient from the center point to the edge (full halo radius) + CGPoint centerPoint = CGPointMake(haloRadius, haloRadius); + CGContextDrawRadialGradient(context, gradient, + centerPoint, 0.0, + centerPoint, haloRadius, + kNilOptions); + + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + CGGradientRelease(gradient); + CGColorSpaceRelease(colorSpace); + + return image.CGImage; +} + +- (CGPathRef)clippingMaskForAccuracy:(CGFloat)accuracy +{ + // size the mask using accuracy, but keep within a good display range + CGFloat clippingDegrees = 90 - accuracy; + clippingDegrees = fmin(clippingDegrees, 70); // most accurate + clippingDegrees = fmax(clippingDegrees, 10); // least accurate + + CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize); + UIBezierPath *ovalPath = UIBezierPath.bezierPath; + + // clip the oval to ± incoming accuracy degrees (converted to radians), from the top + [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)) + radius:CGRectGetWidth(ovalRect) / 2.0 + startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees) + endAngle:MGLRadiansFromDegrees(-clippingDegrees) + clockwise:YES]; + + [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))]; + [ovalPath closePath]; + + return ovalPath.CGPath; +} + +@end -- cgit v1.2.1 From 5db97ed96ee1b6d5fa7b4ae6065cf5399ae1e2c0 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 10 Aug 2017 14:27:27 -0400 Subject: [ios] Introduce MGLUserLocationHeadingArrowLayer class --- .../ios/src/MGLFaux3DUserLocationAnnotationView.m | 7 ++- .../ios/src/MGLUserLocationHeadingArrowLayer.h | 9 ++++ .../ios/src/MGLUserLocationHeadingArrowLayer.m | 55 ++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 platform/ios/src/MGLUserLocationHeadingArrowLayer.h create mode 100644 platform/ios/src/MGLUserLocationHeadingArrowLayer.m (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 36c5292127..a04ac8cd28 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -3,6 +3,7 @@ #import "MGLMapView.h" #import "MGLUserLocation.h" #import "MGLUserLocationHeadingBeamLayer.h" +#import "MGLUserLocationHeadingArrowLayer.h" @implementation MGLFaux3DUserLocationAnnotationView { @@ -12,6 +13,7 @@ CAShapeLayer *_puckArrow; MGLUserLocationHeadingBeamLayer *_headingIndicatorLayer; + //MGLUserLocationHeadingArrowLayer *_headingIndicatorLayer; CALayer *_accuracyRingLayer; CALayer *_dotBorderLayer; CALayer *_dotLayer; @@ -213,7 +215,7 @@ [self updateFrameWithSize:MGLUserLocationAnnotationDotSize]; } - BOOL showHeadingIndicator = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading; + BOOL showHeadingIndicator = YES;//self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading; // update heading indicator // @@ -227,13 +229,14 @@ if ( ! _headingIndicatorLayer && headingAccuracy) { _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self]; + //_headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self]; [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer]; _oldHeadingAccuracy = headingAccuracy; } else if (_oldHeadingAccuracy != headingAccuracy) { - [_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy]; + //[_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy]; _oldHeadingAccuracy = headingAccuracy; } diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.h b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h new file mode 100644 index 0000000000..2b7dea4e62 --- /dev/null +++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h @@ -0,0 +1,9 @@ +#import +#import "MGLUserLocationAnnotationView.h" + +@interface MGLUserLocationHeadingArrowLayer : CAShapeLayer + +- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView; +- (void)updateTintColor:(CGColorRef)color; + +@end diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m new file mode 100644 index 0000000000..3365fddb02 --- /dev/null +++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m @@ -0,0 +1,55 @@ +#import "MGLUserLocationHeadingArrowLayer.h" + +#import "MGLFaux3DUserLocationAnnotationView.h" +#import "MGLGeometry.h" + +const CGFloat MGLUserLocationHeadingArrowSize = 6; + +@implementation MGLUserLocationHeadingArrowLayer + +- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView +{ + CGFloat size = userLocationView.bounds.size.width + MGLUserLocationHeadingArrowSize; + + self = [super init]; + self.bounds = CGRectMake(0, 0, size, size); + self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds)); + self.path = [self arrowPath]; + self.fillColor = userLocationView.tintColor.CGColor; + self.shouldRasterize = YES; + self.rasterizationScale = UIScreen.mainScreen.scale; + self.drawsAsynchronously = YES; + + self.strokeColor = UIColor.whiteColor.CGColor; + self.lineWidth = 1.0; + self.lineJoin = kCALineJoinRound; + + return self; +} + +- (void)updateTintColor:(CGColorRef)color +{ + // redraw the raw tinted gradient + self.fillColor = color; +} + +- (CGPathRef)arrowPath { + CGFloat center = roundf(CGRectGetMidX(self.bounds)); + CGFloat size = MGLUserLocationHeadingArrowSize; + + CGPoint top = CGPointMake(center, 0); + CGPoint left = CGPointMake(center - size, size); + CGPoint right = CGPointMake(center + size, size); + CGPoint middle = CGPointMake(center, size / M_PI); + + UIBezierPath *bezierPath = [UIBezierPath bezierPath]; + [bezierPath moveToPoint:top]; + [bezierPath addLineToPoint:left]; + [bezierPath addQuadCurveToPoint:right controlPoint:middle]; + [bezierPath addLineToPoint:top]; + [bezierPath closePath]; + + return bezierPath.CGPath; +} + +@end -- cgit v1.2.1 From c35b1cd56ba8f4a3d4f195237e025bb24bfa4c43 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 14 Aug 2017 15:27:07 -0400 Subject: [ios] Set a heading indicator update threshold --- platform/ios/src/MGLFaux3DUserLocationAnnotationView.h | 3 +++ platform/ios/src/MGLFaux3DUserLocationAnnotationView.m | 13 +++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h index d5dae3a919..da5c7155a5 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h @@ -7,6 +7,9 @@ const CGFloat MGLUserLocationAnnotationHaloSize = 115.0; const CGFloat MGLUserLocationAnnotationPuckSize = 45.0; const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6; +// Threshold in radians between heading indicator rotation updates. +const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; + @interface MGLFaux3DUserLocationAnnotationView : MGLUserLocationAnnotationView @end diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index a04ac8cd28..ee45865b0a 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -231,10 +231,9 @@ _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self]; //_headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self]; [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer]; - - _oldHeadingAccuracy = headingAccuracy; } - else if (_oldHeadingAccuracy != headingAccuracy) + + if (_oldHeadingAccuracy != headingAccuracy) { //[_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy]; _oldHeadingAccuracy = headingAccuracy; @@ -242,7 +241,13 @@ if (self.userLocation.heading.trueHeading >= 0) { - _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, -MGLRadiansFromDegrees(self.mapView.direction - self.userLocation.heading.trueHeading)); + CGFloat rotation = -MGLRadiansFromDegrees(self.mapView.direction - self.userLocation.heading.trueHeading); + + // Don't rotate if the change is imperceptible. + if (fabs(rotation) > MGLUserLocationHeadingUpdateThreshold) + { + _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, rotation); + } } } else -- cgit v1.2.1 From 99616e662704f3c778d002fc711ec1288fd6d142 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 30 Aug 2017 12:35:20 -0400 Subject: [ios] Introduce MGLUserLocationHeadingIndicator protocol --- .../ios/src/MGLFaux3DUserLocationAnnotationView.m | 44 ++++++++++++++-------- .../ios/src/MGLUserLocationHeadingArrowLayer.h | 4 +- .../ios/src/MGLUserLocationHeadingArrowLayer.m | 6 ++- platform/ios/src/MGLUserLocationHeadingBeamLayer.h | 5 ++- platform/ios/src/MGLUserLocationHeadingIndicator.h | 10 +++++ 5 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 platform/ios/src/MGLUserLocationHeadingIndicator.h (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index ee45865b0a..158bb01d8e 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -2,8 +2,9 @@ #import "MGLMapView.h" #import "MGLUserLocation.h" -#import "MGLUserLocationHeadingBeamLayer.h" +#import "MGLUserLocationHeadingIndicator.h" #import "MGLUserLocationHeadingArrowLayer.h" +#import "MGLUserLocationHeadingBeamLayer.h" @implementation MGLFaux3DUserLocationAnnotationView { @@ -12,14 +13,13 @@ CALayer *_puckDot; CAShapeLayer *_puckArrow; - MGLUserLocationHeadingBeamLayer *_headingIndicatorLayer; - //MGLUserLocationHeadingArrowLayer *_headingIndicatorLayer; + CALayer *_headingIndicatorLayer; CALayer *_accuracyRingLayer; CALayer *_dotBorderLayer; CALayer *_dotLayer; CALayer *_haloLayer; - double _oldHeadingAccuracy; + CLLocationDirection _oldHeadingAccuracy; CLLocationAccuracy _oldHorizontalAccuracy; double _oldZoom; double _oldPitch; @@ -215,28 +215,43 @@ [self updateFrameWithSize:MGLUserLocationAnnotationDotSize]; } - BOOL showHeadingIndicator = YES;//self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading; - - // update heading indicator + // heading indicator (tinted, beam or arrow) // + BOOL headingTrackingModeEnabled = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading; + BOOL showHeadingIndicator = self.mapView.showsUserHeadingIndicator || headingTrackingModeEnabled; + if (showHeadingIndicator) { _headingIndicatorLayer.hidden = NO; CLLocationDirection headingAccuracy = self.userLocation.heading.headingAccuracy; - // heading indicator (tinted, semi-circle) - // + if (([_headingIndicatorLayer isMemberOfClass:[MGLUserLocationHeadingBeamLayer class]] && ! headingTrackingModeEnabled) || + ([_headingIndicatorLayer isMemberOfClass:[MGLUserLocationHeadingArrowLayer class]] && headingTrackingModeEnabled)) + { + [_headingIndicatorLayer removeFromSuperlayer]; + _headingIndicatorLayer = nil; + _oldHeadingAccuracy = -1; + } + if ( ! _headingIndicatorLayer && headingAccuracy) { - _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self]; - //_headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self]; - [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer]; + if (headingTrackingModeEnabled) + { + _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self]; + [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer]; + } + else + { + _headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self]; + [self.layer addSublayer:_headingIndicatorLayer]; + _headingIndicatorLayer.zPosition = 1; + } } if (_oldHeadingAccuracy != headingAccuracy) { - //[_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy]; - _oldHeadingAccuracy = headingAccuracy; + [_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy]; + _oldHeadingAccuracy = headingAccuracy; } if (self.userLocation.heading.trueHeading >= 0) @@ -256,7 +271,6 @@ _headingIndicatorLayer = nil; } - // update accuracy ring (if zoom or horizontal accuracy have changed) // if (_accuracyRingLayer && (_oldZoom != self.mapView.zoomLevel || _oldHorizontalAccuracy != self.userLocation.location.horizontalAccuracy)) diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.h b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h index 2b7dea4e62..6c01356944 100644 --- a/platform/ios/src/MGLUserLocationHeadingArrowLayer.h +++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h @@ -1,9 +1,11 @@ #import #import "MGLUserLocationAnnotationView.h" +#import "MGLUserLocationHeadingIndicator.h" -@interface MGLUserLocationHeadingArrowLayer : CAShapeLayer +@interface MGLUserLocationHeadingArrowLayer : CAShapeLayer - (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView; +- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy; - (void)updateTintColor:(CGColorRef)color; @end diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m index 3365fddb02..912ce30c35 100644 --- a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m +++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m @@ -27,9 +27,13 @@ const CGFloat MGLUserLocationHeadingArrowSize = 6; return self; } +- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy +{ + // unimplemented +} + - (void)updateTintColor:(CGColorRef)color { - // redraw the raw tinted gradient self.fillColor = color; } diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.h b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h index 9a5d4a8f14..93f8ea17ab 100644 --- a/platform/ios/src/MGLUserLocationHeadingBeamLayer.h +++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h @@ -1,9 +1,10 @@ #import #import "MGLUserLocationAnnotationView.h" +#import "MGLUserLocationHeadingIndicator.h" -@interface MGLUserLocationHeadingBeamLayer : CALayer +@interface MGLUserLocationHeadingBeamLayer : CALayer -- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView; +- (MGLUserLocationHeadingBeamLayer *)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView; - (void)updateHeadingAccuracy:(CLLocationDirection)accuracy; - (void)updateTintColor:(CGColorRef)color; diff --git a/platform/ios/src/MGLUserLocationHeadingIndicator.h b/platform/ios/src/MGLUserLocationHeadingIndicator.h new file mode 100644 index 0000000000..61476b96a2 --- /dev/null +++ b/platform/ios/src/MGLUserLocationHeadingIndicator.h @@ -0,0 +1,10 @@ +#import +#import "MGLUserLocationAnnotationView.h" + +@protocol MGLUserLocationHeadingIndicator + +- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView; +- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy; +- (void)updateTintColor:(CGColorRef)color; + +@end -- cgit v1.2.1 From 218c720e9e8882e3d9070ce72ca3b4173c7835c8 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 29 Aug 2017 17:42:38 -0400 Subject: [ios] Add setShowsUserHeadingIndicator and validateUserHeadingUpdating --- platform/ios/src/MGLMapView.h | 17 +++++++++++++++ platform/ios/src/MGLMapView.mm | 47 ++++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 13 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index b475a21d2b..16a76ebcfe 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -357,6 +357,23 @@ MGL_EXPORT IB_DESIGNABLE */ - (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated; +/** + A Boolean value indicating whether the user location annotation may display a + permanent heading indicator. + + Setting this property to `YES` causes the default user location annotation to + always show an arrow-shaped heading indicator, if heading is available. This + property does not rotate the map; for that, see + `MGLUserTrackingModeFollowWithHeading`. + + This property has no effect when `userTrackingMode` is + `MGLUserTrackingModeFollowWithHeading` or + `MGLUserTrackingModeFollowWithCourse`. + + The default value of this property is `NO`. + */ +@property (nonatomic, assign) BOOL showsUserHeadingIndicator; + /** Whether the map view should display a heading calibration alert when necessary. The default value is `YES`. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 320c4bcda8..9f5ccaaa77 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -872,10 +872,14 @@ public: if (self.compassView.alpha) { - [self updateHeadingForDeviceOrientation]; [self updateCompass]; } + if (self.compassView.alpha || self.showsUserHeadingIndicator) + { + [self updateHeadingForDeviceOrientation]; + } + [self updateUserLocationAnnotationView]; } @@ -4195,10 +4199,7 @@ public: self.locationManager.delegate = self; [self.locationManager startUpdatingLocation]; - if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading) - { - [self.locationManager startUpdatingHeading]; - } + [self validateUserHeadingUpdating]; } else if ( ! shouldEnableLocationServices && self.locationManager) { @@ -4322,8 +4323,6 @@ public: { self.userTrackingState = MGLUserTrackingStatePossible; - [self.locationManager stopUpdatingHeading]; - // Immediately update the annotation view; other cases update inside // the locationManager:didUpdateLocations: method. [self updateUserLocationAnnotationView]; @@ -4336,8 +4335,6 @@ public: self.userTrackingState = animated ? MGLUserTrackingStatePossible : MGLUserTrackingStateChanged; self.showsUserLocation = YES; - [self.locationManager stopUpdatingHeading]; - break; } case MGLUserTrackingModeFollowWithHeading: @@ -4354,10 +4351,6 @@ public: [self setZoomLevel:self.currentMinimumZoom animated:YES]; } - [self updateHeadingForDeviceOrientation]; - - [self.locationManager startUpdatingHeading]; - break; } } @@ -4371,6 +4364,8 @@ public: } } + [self validateUserHeadingUpdating]; + if ([self.delegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)]) { [self.delegate mapView:self didChangeUserTrackingMode:_userTrackingMode animated:animated]; @@ -4419,6 +4414,27 @@ public: } } +- (void)setShowsUserHeadingIndicator:(BOOL)showsUserHeadingIndicator +{ + _showsUserHeadingIndicator = showsUserHeadingIndicator; + [self validateUserHeadingUpdating]; +} + +- (void)validateUserHeadingUpdating +{ + BOOL canShowPermanentHeadingIndicator = self.showsUserHeadingIndicator && self.userTrackingMode != MGLUserTrackingModeFollowWithCourse; + + if (self.showsUserLocation && (canShowPermanentHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)) + { + [self updateHeadingForDeviceOrientation]; + [self.locationManager startUpdatingHeading]; + } + else + { + [self.locationManager stopUpdatingHeading]; + } +} + - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { [self locationManager:manager didUpdateLocations:locations animated:YES]; @@ -4666,6 +4682,11 @@ public: self.userLocation.heading = newHeading; + if (self.showsUserHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading) + { + [self updateUserLocationAnnotationView]; + } + if ([self.delegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)]) { [self.delegate mapView:self didUpdateUserLocation:self.userLocation]; -- cgit v1.2.1 From e1ff826d2023eb2b3f366353e960057768bcc855 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Sep 2017 19:05:07 -0400 Subject: =?UTF-8?q?[ios]=20Change=20user=20location=20heading=20filter=20t?= =?UTF-8?q?o=201=C2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default is 1. --- platform/ios/src/MGLMapView.mm | 1 - 1 file changed, 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9f5ccaaa77..6121827d6c 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4195,7 +4195,6 @@ public: } } - self.locationManager.headingFilter = 5.0; self.locationManager.delegate = self; [self.locationManager startUpdatingLocation]; -- cgit v1.2.1 From 63250baa4e73516f300e84ddbc882fc8d41276ef Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 1 Sep 2017 15:10:26 -0400 Subject: [ios] Make user heading indicator property IBInspectable Rename it to `showsHeading` to fit in the tiny label column of the utilities panel. --- platform/ios/src/MGLMapView+IBAdditions.h | 1 + platform/ios/src/MGLMapView.mm | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView+IBAdditions.h b/platform/ios/src/MGLMapView+IBAdditions.h index 3766d044d8..d02c938c57 100644 --- a/platform/ios/src/MGLMapView+IBAdditions.h +++ b/platform/ios/src/MGLMapView+IBAdditions.h @@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) IBInspectable BOOL allowsRotating; @property (nonatomic) IBInspectable BOOL allowsTilting; @property (nonatomic) IBInspectable BOOL showsUserLocation; +@property (nonatomic) IBInspectable BOOL showsHeading; @end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 6121827d6c..e6d10f3479 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -5775,4 +5775,19 @@ private: self.pitchEnabled = allowsTilting; } ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingShowsHeading +{ + return [NSSet setWithObject:@"showsUserHeadingIndicator"]; +} + +- (BOOL)showsHeading +{ + return self.showsUserHeadingIndicator; +} + +- (void)setShowsHeading:(BOOL)showsHeading +{ + self.showsUserHeadingIndicator = showsHeading; +} + @end -- cgit v1.2.1 From cb1309a253b7a9dd203817510a56cee0f0157889 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Sep 2017 15:10:46 -0400 Subject: [ios] Implicitly enable showsUserLocation when enabling the heading indicator --- platform/ios/src/MGLMapView.h | 4 ++-- platform/ios/src/MGLMapView.mm | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 16a76ebcfe..33d1146943 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -362,8 +362,8 @@ MGL_EXPORT IB_DESIGNABLE permanent heading indicator. Setting this property to `YES` causes the default user location annotation to - always show an arrow-shaped heading indicator, if heading is available. This - property does not rotate the map; for that, see + appear and always show an arrow-shaped heading indicator, if heading is + available. This property does not rotate the map; for that, see `MGLUserTrackingModeFollowWithHeading`. This property has no effect when `userTrackingMode` is diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index e6d10f3479..7d47da694c 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4416,6 +4416,12 @@ public: - (void)setShowsUserHeadingIndicator:(BOOL)showsUserHeadingIndicator { _showsUserHeadingIndicator = showsUserHeadingIndicator; + + if (_showsUserHeadingIndicator) + { + self.showsUserLocation = YES; + } + [self validateUserHeadingUpdating]; } @@ -4423,7 +4429,7 @@ public: { BOOL canShowPermanentHeadingIndicator = self.showsUserHeadingIndicator && self.userTrackingMode != MGLUserTrackingModeFollowWithCourse; - if (self.showsUserLocation && (canShowPermanentHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)) + if (canShowPermanentHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading) { [self updateHeadingForDeviceOrientation]; [self.locationManager startUpdatingHeading]; -- cgit v1.2.1 From 59076c830051e1fde26c6353406b04748a7c9b00 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Sep 2017 16:24:26 -0400 Subject: [ios] Disable implicit animation of heading indicators The update steps for the heading indicator are typically small, so animations tend to pile up and cause performance issues. Disabling actions is a slight regression when it comes to large steps (they're not animated now, where they previously were) and this should eventually be addressed. Also consistently use provided API for disabling CATransaction actions. --- platform/ios/src/MGLFaux3DUserLocationAnnotationView.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 158bb01d8e..7001569b30 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -71,7 +71,7 @@ { // disable implicit animation [CATransaction begin]; - [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + [CATransaction setDisableActions:YES]; CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(self.mapView.camera.pitch), 1.0, 0, 0); self.layer.sublayerTransform = t; @@ -261,7 +261,12 @@ // Don't rotate if the change is imperceptible. if (fabs(rotation) > MGLUserLocationHeadingUpdateThreshold) { + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, rotation); + + [CATransaction commit]; } } } @@ -283,10 +288,10 @@ _accuracyRingLayer.hidden = NO; // disable implicit animation of the accuracy ring, unless triggered by a change in accuracy - id shouldDisableActions = (_oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy) ? (id)kCFBooleanTrue : (id)kCFBooleanFalse; + BOOL shouldDisableActions = _oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy; [CATransaction begin]; - [CATransaction setValue:shouldDisableActions forKey:kCATransactionDisableActions]; + [CATransaction setDisableActions:shouldDisableActions]; _accuracyRingLayer.bounds = CGRectMake(0, 0, accuracyRingSize, accuracyRingSize); _accuracyRingLayer.cornerRadius = accuracyRingSize / 2; -- cgit v1.2.1 From 73c633e22b25f40b15dfa3033f666fd65a0bdebf Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Wed, 13 Sep 2017 11:09:49 -0700 Subject: [ios] Ignore Smart Invert (#9876) --- platform/ios/src/MGLMapView.mm | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 7d47da694c..e056f09d22 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -472,12 +472,15 @@ public: _selectedAnnotationTag = MGLAnnotationTagNotFound; _annotationsNearbyLastTap = {}; - // setup logo bug + // setup logo // UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox"]; _logoView = [[UIImageView alloc] initWithImage:logo]; _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if ([_logoView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _logoView.accessibilityIgnoresInvertColors = YES; } +#endif [self addSubview:_logoView]; // setup attribution @@ -485,6 +488,9 @@ public: _attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight]; _attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label"); _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint"); +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if ([_attributionButton respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _attributionButton.accessibilityIgnoresInvertColors = YES; } +#endif [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:_attributionButton]; [_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL]; @@ -498,11 +504,17 @@ public: _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"); +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if ([_compassView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _compassView.accessibilityIgnoresInvertColors = YES; } +#endif [self addSubview:_compassView]; // setup scale control // _scaleBar = [[MGLScaleBar alloc] init]; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if ([_scaleBar respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _scaleBar.accessibilityIgnoresInvertColors = YES; } +#endif [self addSubview:_scaleBar]; // setup interaction @@ -616,6 +628,9 @@ public: _glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _glView.layer.opaque = _opaque; _glView.delegate = self; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if ([_glView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _glView.accessibilityIgnoresInvertColors = YES; } +#endif [_glView bindDrawable]; [self insertSubview:_glView atIndex:0]; _glView.contentMode = UIViewContentModeCenter; -- cgit v1.2.1 From a6224abc33f599f0352682c411b3831b896fb97c Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Thu, 14 Sep 2017 13:51:05 -0700 Subject: [ios] Use constraints to manage ornament view placement (again) (#9995) This reverts the manual layout triggered by KVO approach in favor of a constraints based approach that the KVO method had previously replaced. Although the manual layout approach worked well in most cases and was cleaner in that it did not manipulate any containing views in the app space, using KVO on UIViewControllers has proven to be dangerous. This also updates the gitshas for KIF and OHHTTPStubs to unbreak UI tests that verify these related changes are working. This also adds an ornament constraint system for iOS 11+ --- platform/ios/src/MGLMapView.mm | 318 +++++++++++++++++++++++++++++------------ 1 file changed, 224 insertions(+), 94 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index e056f09d22..08fec00520 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -136,9 +136,6 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1; const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10); -// Context for KVO observing UILayoutGuides. -static void * MGLLayoutGuidesUpdatedContext = &MGLLayoutGuidesUpdatedContext; - /// Unique identifier representing a single annotation in mbgl. typedef uint32_t MGLAnnotationTag; @@ -240,10 +237,14 @@ public: @property (nonatomic) EAGLContext *context; @property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; +@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; @property (nonatomic, readwrite) UIImageView *compassView; +@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints; @property (nonatomic, readwrite) UIImageView *logoView; +@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; +@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; @property (nonatomic, readwrite) MGLStyle *style; @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @@ -298,8 +299,6 @@ public: NSDate *_userLocationAnimationCompletionDate; /// True if a willChange notification has been issued for shape annotation layers and a didChange notification is pending. BOOL _isChangingAnnotationLayers; - BOOL _isObservingTopLayoutGuide; - BOOL _isObservingBottomLayoutGuide; BOOL _isWaitingForRedundantReachableNotification; BOOL _isTargetingInterfaceBuilder; @@ -478,10 +477,12 @@ public: _logoView = [[UIImageView alloc] initWithImage:logo]; _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); + _logoView.translatesAutoresizingMaskIntoConstraints = NO; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 if ([_logoView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _logoView.accessibilityIgnoresInvertColors = YES; } #endif [self addSubview:_logoView]; + _logoViewConstraints = [NSMutableArray array]; // setup attribution // @@ -492,7 +493,9 @@ public: if ([_attributionButton respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _attributionButton.accessibilityIgnoresInvertColors = YES; } #endif [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside]; + _attributionButton.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_attributionButton]; + _attributionButtonConstraints = [NSMutableArray array]; [_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL]; // setup compass @@ -504,18 +507,22 @@ public: _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; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 if ([_compassView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _compassView.accessibilityIgnoresInvertColors = YES; } #endif [self addSubview:_compassView]; + _compassViewConstraints = [NSMutableArray array]; // setup scale control // _scaleBar = [[MGLScaleBar alloc] init]; + _scaleBar.translatesAutoresizingMaskIntoConstraints = NO; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 if ([_scaleBar respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _scaleBar.accessibilityIgnoresInvertColors = YES; } #endif [self addSubview:_scaleBar]; + _scaleBarConstraints = [NSMutableArray array]; // setup interaction // @@ -678,48 +685,6 @@ public: _isWaitingForRedundantReachableNotification = NO; } -- (void)willMoveToWindow:(UIWindow *)newWindow -{ - [super willMoveToWindow:newWindow]; - - if (newWindow) { - [self addLayoutGuideObserversIfNeeded]; - } else { - [self removeLayoutGuideObserversIfNeeded]; - } -} - -- (void)addLayoutGuideObserversIfNeeded -{ - UIViewController *viewController = self.viewControllerForLayoutGuides; - BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; - - if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) { - [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = YES; - } - - if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { - [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = YES; - } -} - -- (void)removeLayoutGuideObserversIfNeeded -{ - UIViewController *viewController = self.viewControllerForLayoutGuides; - - if (_isObservingTopLayoutGuide) { - [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = NO; - } - - if (_isObservingBottomLayoutGuide) { - [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = NO; - } -} - - (void)dealloc { [_reachability stopNotifier]; @@ -728,8 +693,6 @@ public: [[NSNotificationCenter defaultCenter] removeObserver:self]; [_attributionButton removeObserver:self forKeyPath:@"hidden"]; - [self removeLayoutGuideObserversIfNeeded]; - // Removing the annotations unregisters any outstanding KVO observers. NSArray *annotations = self.annotations; if (annotations) @@ -755,6 +718,18 @@ public: { [EAGLContext setCurrentContext:nil]; } + + [self.compassViewConstraints removeAllObjects]; + self.compassViewConstraints = nil; + + [self.scaleBarConstraints removeAllObjects]; + self.scaleBarConstraints = nil; + + [self.logoViewConstraints removeAllObjects]; + self.logoViewConstraints = nil; + + [self.attributionButtonConstraints removeAllObjects]; + self.attributionButtonConstraints = nil; } - (void)setDelegate:(nullable id)delegate @@ -781,6 +756,7 @@ public: - (void)setFrame:(CGRect)frame { [super setFrame:frame]; + if ( ! CGRectEqualToRect(frame, self.frame)) { [self validateTileCacheSize]; @@ -790,6 +766,7 @@ public: - (void)setBounds:(CGRect)bounds { [super setBounds:bounds]; + if ( ! CGRectEqualToRect(bounds, self.bounds)) { [self validateTileCacheSize]; @@ -837,15 +814,207 @@ 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:5.0 + self.contentInset.top]]; + } + [self.compassViewConstraints addObject: + [NSLayoutConstraint constraintWithItem:self.compassView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:5.0 + self.contentInset.top]]; + [self.compassViewConstraints addObject: + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.compassView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:5.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:5.0 + self.contentInset.top]]; + } + [self.scaleBarConstraints addObject: + [NSLayoutConstraint constraintWithItem:self.scaleBar + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:5.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 + 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 + self.contentInset.bottom]]; + } + [self.attributionButtonConstraints addObject: + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:self.attributionButton + attribute:NSLayoutAttributeBaseline + multiplier:1 + constant:8 + self.contentInset.bottom]]; + + [self.attributionButtonConstraints addObject: + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.attributionButton + attribute:NSLayoutAttributeTrailing + multiplier:1 + constant:8 + self.contentInset.right]]; + [containerView addConstraints:self.attributionButtonConstraints]; +} + - (void)updateConstraints { - [super updateConstraints]; - // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets - // is set to YES, -[MGLMapView adjustContentInset] takes top and bottom layout - // guides into account. To get notified about changes to the layout guides, - // we need to observe their bounds and re-layout accordingly. - [self addLayoutGuideObserversIfNeeded]; +// If compiling with the iOS 11+ SDK +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + // If safeAreaLayoutGuide API exists + if ( [self respondsToSelector:@selector(safeAreaLayoutGuide)] ) { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide; +#pragma clang diagnostic pop + // compass view + [self removeConstraints:self.compassViewConstraints]; + [self.compassViewConstraints removeAllObjects]; + [self.compassViewConstraints addObject:[self.compassView.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor + constant:5.0 + self.contentInset.top]]; + [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.scaleBar.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor + constant:5.0 + self.contentInset.top]]; + [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:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:self.logoView.bottomAnchor + constant:8.0 + self.contentInset.bottom]]; + [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:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:self.attributionButton.bottomAnchor + constant:8.0 + self.contentInset.bottom]]; + [self.attributionButtonConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.attributionButton.rightAnchor + constant:8.0 + self.contentInset.right]]; + [self addConstraints:self.attributionButtonConstraints]; + } else { + [self updateConstraintsPreiOS11]; + } +#else + [self updateConstraintsPreiOS11]; +#endif + + [super updateConstraints]; } - (BOOL)isOpaque @@ -878,8 +1047,6 @@ public: [super layoutSubviews]; [self adjustContentInset]; - - [self layoutOrnaments]; if (!_isTargetingInterfaceBuilder) { _mbglMap->setSize([self size]); @@ -898,39 +1065,6 @@ public: [self updateUserLocationAnnotationView]; } -- (void)layoutOrnaments -{ - // scale bar - self.scaleBar.frame = { - self.contentInset.left+8, - self.contentInset.top+5, - CGRectGetWidth(self.scaleBar.frame), - CGRectGetHeight(self.scaleBar.frame) - }; - - // compass - self.compassView.center = { - .x = CGRectGetWidth(self.bounds)-CGRectGetMidX(self.compassView.bounds)-self.contentInset.right-5, - .y = CGRectGetMidY(self.compassView.bounds)+self.contentInset.top+5 - }; - - // logo bug - self.logoView.frame = { - self.contentInset.left+8, - CGRectGetHeight(self.bounds)-8-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), - CGRectGetWidth(self.logoView.bounds), - CGRectGetHeight(self.logoView.bounds) - }; - - // attribution - self.attributionButton.frame = { - CGRectGetWidth(self.bounds)-CGRectGetWidth(self.attributionButton.bounds)-self.contentInset.right-8, - CGRectGetHeight(self.bounds)-CGRectGetHeight(self.attributionButton.bounds)-self.contentInset.bottom-8, - CGRectGetWidth(self.attributionButton.bounds), - CGRectGetHeight(self.attributionButton.bounds) - }; -} - /// Updates `contentInset` to reflect the current window geometry. - (void)adjustContentInset { @@ -2090,10 +2224,6 @@ public: [self updateCalloutView]; } } - else if (context == MGLLayoutGuidesUpdatedContext && [keyPath isEqualToString:@"bounds"]) - { - [self setNeedsLayout]; - } } + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled -- cgit v1.2.1 From 9613582417dd32c6e943eb23b3aad620a5404f1e Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 15 Sep 2017 18:34:06 -0400 Subject: [ios] Be sure to get a BOOL value for nullable dict keys Otherwise it errors with, "cannot initialize a variable of type 'BOOL' (aka 'signed char') with an rvalue of type 'id _Nullable'." This doesn't seem like it should be necessary. --- platform/ios/src/MGLMapView.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 08fec00520..6acaa3c7f3 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4312,15 +4312,15 @@ public: if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) { BOOL requiresWhenInUseUsageDescription = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){11,0,0}]; - BOOL hasWhenInUseUsageDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]; + BOOL hasWhenInUseUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]; BOOL hasAlwaysUsageDescription; if (requiresWhenInUseUsageDescription) { - hasAlwaysUsageDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] && hasWhenInUseUsageDescription; + hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] && hasWhenInUseUsageDescription; } else { - hasAlwaysUsageDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]; + hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]; } if (hasAlwaysUsageDescription) -- cgit v1.2.1 From a9ddf5b7fd311ffb9215a682ab2387181189071e Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Thu, 21 Sep 2017 14:26:48 -0700 Subject: Preserve depth buffer between 3D layers + optimize render order (#9931) Port of https://github.com/mapbox/mapbox-gl-js/pull/5101: adds a new render pass `Pass3D` before any other rendering wherein we render layers with 3D passes (fill-extrusion layers) to offscreen framebuffers, sharing a depth renderbuffer between those layers in order to render 3D space correctly. Those framebuffers are saved on the RenderLayers and copied back to the map during the translucent pass. Rendering to offscreen framebuffers before we do any clear + draw means we can avoid expensive framebuffer restores. --- platform/ios/src/MGLMapView.mm | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index d9d03ea478..49ef2bfb81 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -5464,6 +5464,10 @@ public: } } + mbgl::Size getFramebufferSize() const override { + return nativeView.framebufferSize; + } + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override { bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; [nativeView cameraWillChangeAnimated:animated]; -- cgit v1.2.1 From 093d9da6ef4d5add137149c956d115f185bd4795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Thu, 28 Sep 2017 15:05:22 -0700 Subject: [ios] Define user dot constants in implementation file --- platform/ios/src/MGLFaux3DUserLocationAnnotationView.h | 10 +++++----- platform/ios/src/MGLFaux3DUserLocationAnnotationView.m | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h index da5c7155a5..35fb31a342 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h @@ -1,14 +1,14 @@ #import #import "MGLUserLocationAnnotationView.h" -const CGFloat MGLUserLocationAnnotationDotSize = 22.0; -const CGFloat MGLUserLocationAnnotationHaloSize = 115.0; +extern const CGFloat MGLUserLocationAnnotationDotSize; +extern const CGFloat MGLUserLocationAnnotationHaloSize; -const CGFloat MGLUserLocationAnnotationPuckSize = 45.0; -const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6; +extern const CGFloat MGLUserLocationAnnotationPuckSize; +extern const CGFloat MGLUserLocationAnnotationArrowSize; // Threshold in radians between heading indicator rotation updates. -const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; +extern const CGFloat MGLUserLocationHeadingUpdateThreshold; @interface MGLFaux3DUserLocationAnnotationView : MGLUserLocationAnnotationView diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 7001569b30..21f6aaa540 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -6,6 +6,14 @@ #import "MGLUserLocationHeadingArrowLayer.h" #import "MGLUserLocationHeadingBeamLayer.h" +const CGFloat MGLUserLocationAnnotationDotSize = 22.0; +const CGFloat MGLUserLocationAnnotationHaloSize = 115.0; + +const CGFloat MGLUserLocationAnnotationPuckSize = 45.0; +const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6; + +const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; + @implementation MGLFaux3DUserLocationAnnotationView { BOOL _puckModeActivated; -- cgit v1.2.1 From 201b7d4811a5370ea18518040aeca07fbb7e7b72 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 29 Sep 2017 12:14:05 -0400 Subject: [ios] Don't disable user tracking when selecting an annotation --- platform/ios/src/MGLMapView.mm | 5 ----- 1 file changed, 5 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index a03a5ad357..006792f4bf 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3943,11 +3943,6 @@ public: if (annotation == self.selectedAnnotation) return; - if (annotation != self.userLocation) - { - self.userTrackingMode = MGLUserTrackingModeNone; - } - [self deselectAnnotation:self.selectedAnnotation animated:NO]; // Add the annotation to the map if it hasn’t been added yet. -- cgit v1.2.1 From 1326a21dddd9f593489202a7f98a12b8af2c5a05 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 28 Sep 2017 16:47:54 -0400 Subject: [ios] Expose -showAttribution: publicly as an IBAction --- platform/ios/src/MGLMapView.h | 12 ++++++++++++ platform/ios/src/MGLMapView.mm | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 528606fd4e..06d958185e 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -253,6 +253,9 @@ MGL_EXPORT IB_DESIGNABLE A view showing legally required copyright notices and telemetry settings, positioned at the bottom-right of the map view. + If you choose to reimplement this view, assign the `-showAttribution:` method + as the action for your view to present the default notices and settings. + @note The Mapbox terms of service, which governs the use of Mapbox-hosted vector tiles and styles, requires these @@ -271,6 +274,15 @@ MGL_EXPORT IB_DESIGNABLE */ @property (nonatomic, readonly) UIButton *attributionButton; +/** + Show the attribution and telemetry action sheet. + + This action is performed when the user taps on the attribution button provided + by default via the `attributionButton` property. If you implement a custom + attribution button, you should add this action to the button. + */ +- (IBAction)showAttribution:(id)sender; + /** Support for style classes has been removed. This property always returns an empty array. */ diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 006792f4bf..3288a93ab4 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -503,7 +503,7 @@ public: #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 if ([_attributionButton respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _attributionButton.accessibilityIgnoresInvertColors = YES; } #endif - [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside]; + [_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside]; _attributionButton.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_attributionButton]; _attributionButtonConstraints = [NSMutableArray array]; @@ -2025,7 +2025,7 @@ public: #pragma mark - Attribution - -- (void)showAttribution +- (void)showAttribution:(__unused id)sender { NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title"); UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title -- cgit v1.2.1 From 076af14b905a99f207db459fa9c7bbf826773082 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 28 Sep 2017 17:03:41 -0400 Subject: [ios] Undocument deprecated style class methods --- platform/ios/src/MGLMapView.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 06d958185e..a1347cc09c 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -283,24 +283,16 @@ MGL_EXPORT IB_DESIGNABLE */ - (IBAction)showAttribution:(id)sender; -/** - Support for style classes has been removed. This property always returns an empty array. - */ +/// :nodoc: Support for style classes has been removed. This property always returns an empty array. @property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional."))); -/** - Support for style classes has been removed. This property always returns NO. - */ +/// :nodoc: Support for style classes has been removed. This property always returns NO. - (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); -/** - Support for style classes has been removed. This property is a no-op. - */ +/// :nodoc: Support for style classes has been removed. This property is a no-op. - (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); -/** - Support for style classes has been removed. This property is a no-op. - */ +/// :nodoc: Support for style classes has been removed. This property is a no-op. - (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); #pragma mark Displaying the User’s Location -- cgit v1.2.1 From f01588cac78b5e5411385faa451080a74320500b Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Wed, 4 Oct 2017 10:03:03 -0400 Subject: [ios, macos] Improve snap shotter documentation. (#10020) * [ios, macos] Improve snap shotter documentation. * [macos] Save snapshots in correct format * [macos] Renamed snapshot item to Export Image * [ios, macos] Clarify Snapshotter documentation. * [ios] Fix snapshot scale * [macOS] Fix snapshotter 4x scaling. * [ios] Fix snapshotter final image scale. * [ios, macos] Update snapshotter size documentation. * [ios, macos] Throw an exception when the snapshotter has already started. * [ios, macos] Add snapshotter header example. * [ios, macos] Use one of the predefined Foundation's exception names. --- platform/ios/src/UIImage+MGLAdditions.h | 2 +- platform/ios/src/UIImage+MGLAdditions.mm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h index 3c179d6324..22bb740242 100644 --- a/platform/ios/src/UIImage+MGLAdditions.h +++ b/platform/ios/src/UIImage+MGLAdditions.h @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage; -- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage; +- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale; - (std::unique_ptr)mgl_styleImageWithIdentifier:(NSString *)identifier; diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm index 7cf1ed9bcc..8ab1d5c259 100644 --- a/platform/ios/src/UIImage+MGLAdditions.mm +++ b/platform/ios/src/UIImage+MGLAdditions.mm @@ -22,14 +22,14 @@ return self; } -- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage +- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale { CGImageRef image = CGImageFromMGLPremultipliedImage(mbglImage.clone()); if (!image) { return nil; } - self = [self initWithCGImage:image scale:1.0 orientation:UIImageOrientationUp]; + self = [self initWithCGImage:image scale:scale orientation:UIImageOrientationUp]; CGImageRelease(image); return self; -- cgit v1.2.1 From 9e79659007131450cde7e6e1a361f8ba3321fe98 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 2 Oct 2017 14:46:25 -0700 Subject: [android][ios][macOS] Implement bindings for `Map::cameraForGeometry`. On macOS, also added -[MGLMapView setCamera: withDuration: animationTimingFunction: edgePadding: completionHandler:] for parity with iOS --- platform/ios/src/MGLMapView.h | 14 ++++++++++++++ platform/ios/src/MGLMapView.mm | 11 +++++++++++ 2 files changed, 25 insertions(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index a1347cc09c..843fb3d280 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -889,6 +889,20 @@ MGL_EXPORT IB_DESIGNABLE */ - (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets; +/** + Returns the camera that best fits the given shape, with the specified direction, + optionally with some additional padding on each side. + + @param shape The shape to fit to the receiver’s viewport. + @param direction The direction of the viewport, measured in degrees clockwise from true north. + @param insets The minimum padding (in screen points) that would be visible + around the returned camera object if it were set as the receiver’s camera. + @return A camera object centered on the shape's center with zoom level as high + (close to the ground) as possible while still including the entire shape. The + camera object uses the current pitch. + */ +- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(double)direction edgePadding:(UIEdgeInsets)insets; + /** Returns the point in this view’s coordinate system on which to "anchor" in response to a user-initiated gesture. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 3288a93ab4..cc6c6bde6d 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -35,6 +35,7 @@ #include #import "Mapbox.h" +#import "MGLShape_Private.h" #import "MGLFeature_Private.h" #import "MGLGeometry_Private.h" #import "MGLMultiPoint_Private.h" @@ -3011,6 +3012,16 @@ public: return [self cameraForCameraOptions:cameraOptions]; } +- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(double)direction edgePadding:(UIEdgeInsets)insets { + mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets); + padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset); + + mbgl::CameraOptions cameraOptions = _mbglMap->cameraForGeometry([shape geometryObject], padding, direction); + + return [self cameraForCameraOptions:cameraOptions]; + +} + - (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions { CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng()); -- cgit v1.2.1 From 24bd336b88b4f2dae25277efeeb36a96061139a3 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 9 Oct 2017 11:50:48 -0700 Subject: [android][ios][macOS] Implement bindings for `Map::cameraForGeometry`. On macOS, also added -[MGLMapView setCamera: withDuration: animationTimingFunction: edgePadding: completionHandler:] for parity with iOS --- platform/ios/src/MGLMapView.h | 14 ++++++++++++++ platform/ios/src/MGLMapView.mm | 11 +++++++++++ 2 files changed, 25 insertions(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index a1347cc09c..e2c070a54f 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -889,6 +889,20 @@ MGL_EXPORT IB_DESIGNABLE */ - (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets; +/** + Returns the camera that best fits the given shape, with the specified direction, + optionally with some additional padding on each side. + + @param shape The shape to fit to the receiver’s viewport. + @param direction The direction of the viewport, measured in degrees clockwise from true north. + @param insets The minimum padding (in screen points) that would be visible + around the returned camera object if it were set as the receiver’s camera. + @return A camera object centered on the shape's center with zoom level as high + (close to the ground) as possible while still including the entire shape. The + camera object uses the current pitch. + */ +- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets; + /** Returns the point in this view’s coordinate system on which to "anchor" in response to a user-initiated gesture. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 3288a93ab4..0e76c0c71c 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -35,6 +35,7 @@ #include #import "Mapbox.h" +#import "MGLShape_Private.h" #import "MGLFeature_Private.h" #import "MGLGeometry_Private.h" #import "MGLMultiPoint_Private.h" @@ -3011,6 +3012,16 @@ public: return [self cameraForCameraOptions:cameraOptions]; } +- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets { + mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets); + padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset); + + mbgl::CameraOptions cameraOptions = _mbglMap->cameraForGeometry([shape geometryObject], padding, direction); + + return [self cameraForCameraOptions:cameraOptions]; + +} + - (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions { CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng()); -- cgit v1.2.1 From 98a47884f06a8f165a2c15a54f82b356c8ef23d8 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 9 Oct 2017 15:59:07 -0700 Subject: [ios, macos] Use CLLocationDirection for direction parameter in -[MGLMapView cameraThatFitsShape:shape direction: edgePadding:] --- platform/ios/src/MGLMapView.h | 2 +- platform/ios/src/MGLMapView.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 843fb3d280..e2c070a54f 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -901,7 +901,7 @@ MGL_EXPORT IB_DESIGNABLE (close to the ground) as possible while still including the entire shape. The camera object uses the current pitch. */ -- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(double)direction edgePadding:(UIEdgeInsets)insets; +- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets; /** Returns the point in this view’s coordinate system on which to "anchor" in diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index cc6c6bde6d..0e76c0c71c 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3012,7 +3012,7 @@ public: return [self cameraForCameraOptions:cameraOptions]; } -- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(double)direction edgePadding:(UIEdgeInsets)insets { +- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets { mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets); padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset); -- cgit v1.2.1 From b7879d5a370f232b3030fb404a9eb8a51d3a7eb1 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 11 Oct 2017 10:06:30 -0700 Subject: [ios] Update MGLUserLocation.heading for showsUserHeadingIndicator --- platform/ios/src/MGLUserLocation.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h index 91abadbcb7..4e01cf00c9 100644 --- a/platform/ios/src/MGLUserLocation.h +++ b/platform/ios/src/MGLUserLocation.h @@ -34,7 +34,8 @@ MGL_EXPORT The heading of the user location. (read-only) This property is `nil` if the user location tracking mode is not - `MGLUserTrackingModeFollowWithHeading`. + `MGLUserTrackingModeFollowWithHeading` or if + `MGLMapView.showsUserHeadingIndicator` is disabled. */ @property (nonatomic, readonly, nullable) CLHeading *heading; -- cgit v1.2.1 From 5cefa515112c538fbb39aca1aa985c13a8299dc4 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 11 Oct 2017 11:47:05 -0700 Subject: [ios] Rename SMCalloutView and stop using submodule --- platform/ios/src/MGLCompactCalloutView.h | 2 +- platform/ios/src/MGLMapView.mm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLCompactCalloutView.h b/platform/ios/src/MGLCompactCalloutView.h index 56c48a99e5..5cecf37ff6 100644 --- a/platform/ios/src/MGLCompactCalloutView.h +++ b/platform/ios/src/MGLCompactCalloutView.h @@ -7,7 +7,7 @@ callout view displays the represented annotation’s title, subtitle, and accessory views in a compact, two-line layout. */ -@interface MGLCompactCalloutView : SMCalloutView +@interface MGLCompactCalloutView : MGLSMCalloutView + (instancetype)platformCalloutView; diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 0e76c0c71c..ac608dd074 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -232,7 +232,7 @@ public: @interface MGLMapView () @@ -1929,7 +1929,7 @@ public: return [self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)]; } -- (void)calloutViewClicked:(__unused SMCalloutView *)calloutView +- (void)calloutViewClicked:(__unused MGLSMCalloutView *)calloutView { if ([self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)]) { -- cgit v1.2.1 From b7b8baef9d30d31a757dbbc0fbb8cb2c5cf3585e Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Wed, 18 Oct 2017 15:18:54 -0400 Subject: [ios, macos] Add selection support to MGLMultiPoint annotations. (#9984) * [ios, macos] Add selection support to MGLMultiPoint annotations. * [ios, macos] Update changelogs. --- platform/ios/src/MGLMapView.mm | 107 ++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 40 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index ac608dd074..4112df6e76 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3257,6 +3257,12 @@ public: } std::vector annotationTags = [self annotationTagsInRect:rect]; + std::vector shapeAnnotationTags = [self shapeAnnotationTagsInRect:rect]; + + if (shapeAnnotationTags.size()) { + annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end()); + } + if (annotationTags.size()) { NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()]; @@ -3762,6 +3768,12 @@ public: queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); std::vector nearbyAnnotations = [self annotationTagsInRect:queryRect]; + BOOL queryingShapeAnnotations = NO; + + if (!nearbyAnnotations.size()) { + nearbyAnnotations = [self shapeAnnotationTagsInRect:queryRect]; + queryingShapeAnnotations = YES; + } if (nearbyAnnotations.size()) { @@ -3769,54 +3781,56 @@ public: CGRect hitRect = CGRectInset({ point, CGSizeZero }, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); - - // Filter out any annotation whose image or view is unselectable or for which - // hit testing fails. - auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), - [&](const MGLAnnotationTag annotationTag) - { - id annotation = [self annotationWithTag:annotationTag]; - NSAssert(annotation, @"Unknown annotation found nearby tap"); - if ( ! annotation) - { - return true; - } - - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - CGRect annotationRect; - - MGLAnnotationView *annotationView = annotationContext.annotationView; - if (annotationView) - { - if ( ! annotationView.enabled) + + if (!queryingShapeAnnotations) { + // Filter out any annotation whose image or view is unselectable or for which + // hit testing fails. + auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag annotationTag) { + id annotation = [self annotationWithTag:annotationTag]; + NSAssert(annotation, @"Unknown annotation found nearby tap"); + if ( ! annotation) { return true; } - CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self]; - CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); - annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); - } - else - { - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - if ( ! annotationImage.enabled) + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + CGRect annotationRect; + + MGLAnnotationView *annotationView = annotationContext.annotationView; + + if (annotationView) { - return true; + if ( ! annotationView.enabled) + { + return true; + } + + CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self]; + CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); + annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); } + else + { + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + if ( ! annotationImage.enabled) + { + return true; + } - MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; - UIImage *fallbackImage = fallbackAnnotationImage.image; + MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; + UIImage *fallbackImage = fallbackAnnotationImage.image; - annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; - } + annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; + } - // Filter out the annotation if the fattened finger didn’t land - // within the image’s alignment rect. - return !!!CGRectIntersectsRect(annotationRect, hitRect); - }); + // Filter out the annotation if the fattened finger didn’t land + // within the image’s alignment rect. + return !!!CGRectIntersectsRect(annotationRect, hitRect); + }); + + nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); + } - nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); } MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound; @@ -3899,6 +3913,14 @@ public: }); } +- (std::vector)shapeAnnotationTagsInRect:(CGRect)rect +{ + return _rendererFrontend->getRenderer()->queryShapeAnnotations({ + { CGRectGetMinX(rect), CGRectGetMinY(rect) }, + { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, + }); +} + - (id )selectedAnnotation { if (_userLocationAnnotationIsSelected) @@ -3950,8 +3972,6 @@ public: { if ( ! annotation) return; - if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; - if (annotation == self.selectedAnnotation) return; [self deselectAnnotation:self.selectedAnnotation animated:NO]; @@ -4095,6 +4115,13 @@ public: { return CGRectZero; } + + if ([annotation isKindOfClass:[MGLMultiPoint class]]) { + CLLocationCoordinate2D origin = annotation.coordinate; + CGPoint originPoint = [self convertCoordinate:origin toPointToView:self]; + return CGRectMake(originPoint.x, originPoint.y, MGLAnnotationImagePaddingForHitTest, MGLAnnotationImagePaddingForHitTest); + + } UIImage *image = [self imageOfAnnotationWithTag:annotationTag].image; if ( ! image) { -- cgit v1.2.1 From 6523b16c61d988960e24713d0198685801147c45 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 3 Oct 2017 15:38:08 -0700 Subject: [ios] Slightly round the corners of the puck arrow --- platform/ios/src/MGLFaux3DUserLocationAnnotationView.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 21f6aaa540..63e754d9c6 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -10,7 +10,7 @@ const CGFloat MGLUserLocationAnnotationDotSize = 22.0; const CGFloat MGLUserLocationAnnotationHaloSize = 115.0; const CGFloat MGLUserLocationAnnotationPuckSize = 45.0; -const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6; +const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.5; const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; @@ -181,6 +181,10 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; _puckArrow.rasterizationScale = [UIScreen mainScreen].scale; _puckArrow.drawsAsynchronously = YES; + _puckArrow.lineJoin = @"round"; + _puckArrow.lineWidth = 1.f; + _puckArrow.strokeColor = _puckArrow.fillColor; + [self.layer addSublayer:_puckArrow]; } if (self.userLocation.location.course >= 0) -- cgit v1.2.1 From cabf5d2970148d47b9b1ecba02bfa1f36e1f94ac Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 3 Oct 2017 18:08:13 -0700 Subject: [ios] Avoid drawing view annotations across pixel boundaries --- platform/ios/src/MGLFaux3DUserLocationAnnotationView.m | 12 +++++++----- platform/ios/src/MGLMapView.mm | 11 +++++------ 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m index 63e754d9c6..1ed3d86ad1 100644 --- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m +++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m @@ -175,8 +175,8 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; _puckArrow = [CAShapeLayer layer]; _puckArrow.path = [[self puckArrow] CGPath]; _puckArrow.fillColor = [self.mapView.tintColor CGColor]; - _puckArrow.bounds = CGRectMake(0, 0, MGLUserLocationAnnotationArrowSize, MGLUserLocationAnnotationArrowSize); - _puckArrow.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0); + _puckArrow.bounds = CGRectMake(0, 0, round(MGLUserLocationAnnotationArrowSize), round(MGLUserLocationAnnotationArrowSize)); + _puckArrow.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds)); _puckArrow.shouldRasterize = YES; _puckArrow.rasterizationScale = [UIScreen mainScreen].scale; _puckArrow.drawsAsynchronously = YES; @@ -306,7 +306,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; [CATransaction setDisableActions:shouldDisableActions]; _accuracyRingLayer.bounds = CGRectMake(0, 0, accuracyRingSize, accuracyRingSize); - _accuracyRingLayer.cornerRadius = accuracyRingSize / 2; + _accuracyRingLayer.cornerRadius = accuracyRingSize / 2.0; // match the halo to the accuracy ring _haloLayer.bounds = _accuracyRingLayer.bounds; @@ -435,9 +435,11 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; - (CALayer *)circleLayerWithSize:(CGFloat)layerSize { + layerSize = round(layerSize); + CALayer *circleLayer = [CALayer layer]; circleLayer.bounds = CGRectMake(0, 0, layerSize, layerSize); - circleLayer.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0); + circleLayer.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds)); circleLayer.cornerRadius = layerSize / 2.0; circleLayer.shouldRasterize = YES; circleLayer.rasterizationScale = [UIScreen mainScreen].scale; @@ -460,7 +462,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01; - (CGFloat)calculateAccuracyRingSize { // diameter in screen points - return self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0; + return round(self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0); } @end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 4112df6e76..d0d34dbfa9 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3379,7 +3379,7 @@ public: { annotationViewsForAnnotation[annotationValue] = annotationView; annotationView.annotation = annotation; - annotationView.center = [self convertCoordinate:annotation.coordinate toPointToView:self]; + annotationView.center = MGLPointRounded([self convertCoordinate:annotation.coordinate toPointToView:self]); [newAnnotationViews addObject:annotationView]; MGLAnnotationImage *annotationImage = self.invisibleAnnotationImage; @@ -3805,7 +3805,7 @@ public: return true; } - CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self]; + CGPoint calloutAnchorPoint = MGLPointRounded([self convertCoordinate:annotation.coordinate toPointToView:self]); CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); } @@ -4143,7 +4143,7 @@ public: /// image centered at the given coordinate. - (CGRect)frameOfImage:(UIImage *)image centeredAtCoordinate:(CLLocationCoordinate2D)coordinate { - CGPoint calloutAnchorPoint = [self convertCoordinate:coordinate toPointToView:self]; + CGPoint calloutAnchorPoint = MGLPointRounded([self convertCoordinate:coordinate toPointToView:self]); CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -image.size.width / 2, -image.size.height / 2); return UIEdgeInsetsInsetRect(frame, image.alignmentRectInsets); } @@ -4582,7 +4582,6 @@ public: if (_showsUserHeadingIndicator) { self.showsUserLocation = YES; - } [self validateUserHeadingUpdating]; } @@ -5288,7 +5287,7 @@ public: if (annotationView) { - annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]; + annotationView.center = MGLPointRounded([self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]); } } @@ -5406,7 +5405,7 @@ public: } else { - userPoint = [self convertCoordinate:self.userLocation.coordinate toPointToView:self]; + userPoint = MGLPointRounded([self convertCoordinate:self.userLocation.coordinate toPointToView:self]); } if ( ! annotationView.superview) -- cgit v1.2.1 From 817e5c109f5e48e0ce05b05058498cfa6be8e2da Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 17 Oct 2017 20:56:22 -0400 Subject: [ios] Fix scale bar label alignment --- platform/ios/src/MGLScaleBar.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 410aa7d57e..f63c39009f 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -334,7 +334,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084; } - (void)layoutBars { - CGFloat barWidth = (CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count; + CGFloat barWidth = round((CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count); NSUInteger i = 0; for (UIView *bar in self.bars) { @@ -357,11 +357,11 @@ static const CGFloat MGLFeetPerMeter = 3.28084; } - (void)layoutLabels { - CGFloat barWidth = self.bounds.size.width / self.bars.count; + CGFloat barWidth = round(self.bounds.size.width / self.bars.count); BOOL RTL = [self usesRightToLeftLayout]; NSUInteger i = RTL ? self.bars.count : 0; for (MGLScaleBarLabel *label in self.labels) { - CGFloat xPosition = barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth; + CGFloat xPosition = round(barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth); label.frame = CGRectMake(xPosition, 0, CGRectGetWidth(label.bounds), CGRectGetHeight(label.bounds)); -- cgit v1.2.1 From 2857090cf6c002e876be2b8c2826db99e7c40183 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 19 Oct 2017 11:40:30 -0400 Subject: [ios] Fix iOS 8 incompatibility in scale bar RTL check --- platform/ios/src/MGLScaleBar.mm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index f63c39009f..1cf1caf0fe 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -175,10 +175,15 @@ static const CGFloat MGLFeetPerMeter = 3.28084; return [self usesMetricSystem] ? self.metersPerPoint : self.metersPerPoint * MGLFeetPerMeter; } -#pragma mark - Convenient methods +#pragma mark - Convenience methods - (BOOL)usesRightToLeftLayout { - return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; + // semanticContentAttribute is iOS 9+ + if ([self.superview respondsToSelector:@selector(semanticContentAttribute)]) { + return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; + } else { + return UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; + } } - (BOOL)usesMetricSystem { @@ -244,7 +249,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084; CGFloat alpha = maximumDistance > allowedDistance ? .0f : 1.0f; - if(self.alpha != alpha) { + if (self.alpha != alpha) { [UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ self.alpha = alpha; } completion:nil]; -- cgit v1.2.1 From aad26419b4b196cec35d5b349ae3977782e8fa6d Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 24 Oct 2017 17:40:47 -0700 Subject: [ios] Move scale bar content size invalidation to map view --- platform/ios/src/MGLMapView.mm | 8 ++++++++ platform/ios/src/MGLScaleBar.mm | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 0e76c0c71c..2bb46a3638 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1015,6 +1015,14 @@ public: // This gets called when the view dimension changes, e.g. because the device is being rotated. - (void)layoutSubviews { + // Calling this here instead of in the scale bar itself because if this is done in the + // scale bar instance, it triggers a call to this this `layoutSubviews` method that + // calls `_mbglMap->setSize()` just below that triggers rendering update which triggers + // another scale bar update which causes a rendering update loop and a major performace + // degradation. The only time the scale bar's intrinsic content size _must_ invalidated + // is here as a reaction to this object's view dimension changes. + [self.scaleBar invalidateIntrinsicContentSize]; + [super layoutSubviews]; [self adjustContentInset]; diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 410aa7d57e..966555e46a 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -225,9 +225,6 @@ static const CGFloat MGLFeetPerMeter = 3.28084; CGRectGetMinY(self.frame), size.width, size.height); - - [self invalidateIntrinsicContentSize]; - [self setNeedsLayout]; } - (void)updateVisibility { -- cgit v1.2.1 From 91eb595463cf42c85edc7fe5b47023f8d7b75828 Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 24 Oct 2017 17:40:47 -0700 Subject: [ios] Move scale bar content size invalidation to map view --- platform/ios/src/MGLMapView.mm | 8 ++++++++ platform/ios/src/MGLScaleBar.mm | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index d0d34dbfa9..740555840f 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1015,6 +1015,14 @@ public: // This gets called when the view dimension changes, e.g. because the device is being rotated. - (void)layoutSubviews { + // Calling this here instead of in the scale bar itself because if this is done in the + // scale bar instance, it triggers a call to this this `layoutSubviews` method that + // calls `_mbglMap->setSize()` just below that triggers rendering update which triggers + // another scale bar update which causes a rendering update loop and a major performace + // degradation. The only time the scale bar's intrinsic content size _must_ invalidated + // is here as a reaction to this object's view dimension changes. + [self.scaleBar invalidateIntrinsicContentSize]; + [super layoutSubviews]; [self adjustContentInset]; diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 1cf1caf0fe..139dffdfab 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -230,9 +230,6 @@ static const CGFloat MGLFeetPerMeter = 3.28084; CGRectGetMinY(self.frame), size.width, size.height); - - [self invalidateIntrinsicContentSize]; - [self setNeedsLayout]; } - (void)updateVisibility { -- cgit v1.2.1 From 372698aa23d2fb371df6790aa96d83bdf735edc9 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 27 Oct 2017 10:24:12 -0400 Subject: [ios, macos] Iterate over point and shape annotations. (#10262) --- platform/ios/src/MGLMapView.mm | 90 +++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 44 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 740555840f..a7d2e17ce9 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3776,11 +3776,10 @@ public: queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); std::vector nearbyAnnotations = [self annotationTagsInRect:queryRect]; - BOOL queryingShapeAnnotations = NO; + std::vector nearbyShapeAnnotations = [self shapeAnnotationTagsInRect:queryRect]; - if (!nearbyAnnotations.size()) { - nearbyAnnotations = [self shapeAnnotationTagsInRect:queryRect]; - queryingShapeAnnotations = YES; + if (nearbyShapeAnnotations.size()) { + nearbyAnnotations.insert(nearbyAnnotations.end(), nearbyShapeAnnotations.begin(), nearbyShapeAnnotations.end()); } if (nearbyAnnotations.size()) @@ -3790,54 +3789,57 @@ public: -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); - if (!queryingShapeAnnotations) { - // Filter out any annotation whose image or view is unselectable or for which - // hit testing fails. - auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag annotationTag) { - id annotation = [self annotationWithTag:annotationTag]; - NSAssert(annotation, @"Unknown annotation found nearby tap"); - if ( ! annotation) + // Filter out any annotation whose image or view is unselectable or for which + // hit testing fails. + auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag annotationTag) { + id annotation = [self annotationWithTag:annotationTag]; + NSAssert(annotation, @"Unknown annotation found nearby tap"); + if ( ! annotation) + { + return true; + } + + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + CGRect annotationRect; + + MGLAnnotationView *annotationView = annotationContext.annotationView; + + if (annotationView) + { + if ( ! annotationView.enabled) { return true; } - - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - CGRect annotationRect; - - MGLAnnotationView *annotationView = annotationContext.annotationView; - - if (annotationView) + + CGPoint calloutAnchorPoint = MGLPointRounded([self convertCoordinate:annotation.coordinate toPointToView:self]); + CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); + annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); + } + else + { + if ([annotation isKindOfClass:[MGLShape class]]) { - if ( ! annotationView.enabled) - { - return true; - } - - CGPoint calloutAnchorPoint = MGLPointRounded([self convertCoordinate:annotation.coordinate toPointToView:self]); - CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); - annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); + return false; } - else + + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + if ( ! annotationImage.enabled) { - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - if ( ! annotationImage.enabled) - { - return true; - } - - MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; - UIImage *fallbackImage = fallbackAnnotationImage.image; - - annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; + return true; } - - // Filter out the annotation if the fattened finger didn’t land - // within the image’s alignment rect. - return !!!CGRectIntersectsRect(annotationRect, hitRect); - }); + + MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; + UIImage *fallbackImage = fallbackAnnotationImage.image; + + annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; + } - nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); - } + // Filter out the annotation if the fattened finger didn’t land + // within the image’s alignment rect. + return !!!CGRectIntersectsRect(annotationRect, hitRect); + }); + + nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); } -- cgit v1.2.1 From 63f7963a93cb13ec9f7cc404b93bc874419f4624 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Wed, 1 Nov 2017 08:20:48 -0400 Subject: [ios, macos] Center shape annotation's callout when offscreen. (#10255) * [ios, macos] Center annotation's anchor to tap point when coordinate center is offscreen. * [ios, macos] Update changelogs. --- platform/ios/src/MGLMapView.mm | 59 +++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 21 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index a7d2e17ce9..ed51754b0a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1615,7 +1615,9 @@ public: idannotation = [self annotationForGestureRecognizer:singleTap persistingResults:YES]; if(annotation) { - [self selectAnnotation:annotation animated:YES]; + CGPoint calloutPoint = [singleTap locationInView:self]; + CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint]; + [self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect]; } else { @@ -3979,6 +3981,12 @@ public: } - (void)selectAnnotation:(id )annotation animated:(BOOL)animated +{ + CGRect positioningRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:CGPointZero]; + [self selectAnnotation:annotation animated:animated calloutPositioningRect:positioningRect]; +} + +- (void)selectAnnotation:(id )annotation animated:(BOOL)animated calloutPositioningRect:(CGRect)calloutPositioningRect { if ( ! annotation) return; @@ -3995,9 +4003,6 @@ public: if (annotationTag == MGLAnnotationTagNotFound) return; } - // By default attempt to use the GL annotation image frame as the positioning rect. - CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag]; - MGLAnnotationView *annotationView = nil; if (annotation != self.userLocation) @@ -4005,21 +4010,12 @@ public: MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); annotationView = annotationContext.annotationView; if (annotationView && annotationView.enabled) { - { - // Annotations represented by views use the view frame as the positioning rect. - positioningRect = annotationView.frame; - [annotationView.superview bringSubviewToFront:annotationView]; - [annotationView setSelected:YES animated:animated]; + // Annotations represented by views use the view frame as the positioning rect. + calloutPositioningRect = annotationView.frame; + [annotationView.superview bringSubviewToFront:annotationView]; + [annotationView setSelected:YES animated:animated]; } } - } - - // The client can request that any annotation be selected (even ones that are offscreen). - // The annotation can’t be selected if no part of it is hittable. - if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation) - { - return; - } self.selectedAnnotation = annotation; @@ -4049,7 +4045,7 @@ public: if (_userLocationAnnotationIsSelected) { - positioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame]; + calloutPositioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame]; CGRect implicitAnnotationFrame = [self.userLocationAnnotationView.layer.presentationLayer frame]; CGRect explicitAnnotationFrame = self.userLocationAnnotationView.frame; @@ -4065,7 +4061,7 @@ public: if ([calloutView.leftAccessoryView isKindOfClass:[UIControl class]]) { UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(handleCalloutAccessoryTapGesture:)]; + action:@selector(handleCalloutAccessoryTapGesture:)]; [calloutView.leftAccessoryView addGestureRecognizer:calloutAccessoryTap]; } @@ -4078,7 +4074,7 @@ public: if ([calloutView.rightAccessoryView isKindOfClass:[UIControl class]]) { UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(handleCalloutAccessoryTapGesture:)]; + action:@selector(handleCalloutAccessoryTapGesture:)]; [calloutView.rightAccessoryView addGestureRecognizer:calloutAccessoryTap]; } @@ -4088,7 +4084,7 @@ public: calloutView.delegate = self; // present popup - [calloutView presentCalloutFromRect:positioningRect + [calloutView presentCalloutFromRect:calloutPositioningRect inView:self.glView constrainedToView:self.glView animated:animated]; @@ -4115,6 +4111,27 @@ public: return calloutView; } +/// Returns the rectangle that represents the annotation image of the annotation +/// with the given tag. This rectangle is fitted to the image’s alignment rect +/// and is appropriate for positioning a popover. +/// If a shape annotation is visible but its centroid is not, and a default point is specified, +/// the callout view is anchored to the default callout point. +- (CGRect)positioningRectForAnnotation:(id )annotation defaultCalloutPoint:(CGPoint)calloutPoint +{ + MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation]; + CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag]; + + // For annotations which `coordinate` falls offscreen it will use the current tap point as anchor instead. + if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation) + { + if (!CGPointEqualToPoint(calloutPoint, CGPointZero)) { + positioningRect = CGRectMake(calloutPoint.x, calloutPoint.y, positioningRect.size.width, positioningRect.size.height); + } + } + + return positioningRect; +} + /// Returns the rectangle that represents the annotation image of the annotation /// with the given tag. This rectangle is fitted to the image’s alignment rect /// and is appropriate for positioning a popover. -- cgit v1.2.1 From 1262659b8da2a52ce1697372b97b086a61810651 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 30 Oct 2017 16:00:14 +0200 Subject: [core] Move GLContextMode to its own Renderer header --- platform/ios/src/MGLMapView.mm | 1 + 1 file changed, 1 insertion(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 2bb46a3638..d5bfdbbc9f 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.1 From 2955fb594c5235c302773d6fc36d85f05a654290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 3 Nov 2017 12:17:45 -0700 Subject: Make places and roads accessible to VoiceOver (#9950) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ios] Summarize places, roads after zooming with VoiceOver After zooming, MGLMapView’s accessibility value now indicates the number of visible roads and lists out a few places visible in the current viewport, starting with the features at the highest z-index (not necessarily the largest or the closest to the center of the view). Avoid saying that no annotations are visible. * [ios] Allow VoiceOver to navigate among place features Split out a separate header for the various accessibility elements tied to MGLMapView. Wrap place features in accessibility elements and insert them into the narration order after the visible annotations but before the attribution button. Refactored MGLMapView’s accessibility code to rely on ranges to avoid off-by-one errors. * [ios] Post layout change notification when fully rendered Post a layout change notification when fully finishing a map render. * [ios, macos] Moved MGLVectorSource+MGLAdditions to more specific group * [ios] Localize accessibility feature names * [ios] Find place feature accessibility elements by identifier * [ios] Refactored accessibility traits Also created a new MGLPlaceFeatureAccessibilityElement class. * [ios] Sort accessibility elements by screen distance from center Sort annotation accessibility elements by screen distance, not the hypotenuse of coordinates, which can yield incorrect results when the map is rotated or tilted or when the user is located at high latitudes. Sort place feature accessibility elements by screen distance as well. * [ios] Create a place feature accessibility element, not an abstract feature accessibility element * [ios] Only query for visible place features once per camera Improved accessibility performance after changing the map camera. MGLMapView no longer queries the map for place features once per place feature. * [ios] Made roads accessible Wrap visible road features in accessibility elements described by the road name, route number, and general direction of travel. * [ios] Cleaned up radian conversions * [ios] Thickened road accessibility elements * [ios] Made unioned roads accessible * [ios] Consistently sort accessibility elements Also fixed an issue causing road feature accessibility elements to get treated like place feature accessibility elements. * [ios] Announce direction of divided roads Announce the direction of a divided road based on the direction of its first polyline. * [ios] Refined announced elevation units * [ios] Romanize feature names * [ios] Updated changelog * [ios] Delay zoom announcement A 100-millisecond delay is enough for the post-zooming announcement to reflect the new zoom level rather than the previous zoom level. * [ios] Consolidated geometry functions Adopted MGLGeometry_Private.h in the accessibility code, forcing a conversion to Objective-C++. Avoid inlining some of the more complex geometric functions. * [ios] Fixed feature name romanization in accessibility labels NSLocale.scriptCode is only set when the locale identifier explicitly specifies a script. Use NSOrthography to identify the dominant orthography regardless of locale. Also added a unit test of feature accessibility element labels. * [ios] Added tests for place, road accessibility values * [ios] Announce one-way roads A road feature’s accessibility value now indicates whether the road is a one-way road. --- platform/ios/src/MGLMapAccessibilityElement.h | 54 +++ platform/ios/src/MGLMapAccessibilityElement.mm | 199 ++++++++++ platform/ios/src/MGLMapView.mm | 496 +++++++++++++++++++------ 3 files changed, 630 insertions(+), 119 deletions(-) create mode 100644 platform/ios/src/MGLMapAccessibilityElement.h create mode 100644 platform/ios/src/MGLMapAccessibilityElement.mm (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapAccessibilityElement.h b/platform/ios/src/MGLMapAccessibilityElement.h new file mode 100644 index 0000000000..952f6cbf2f --- /dev/null +++ b/platform/ios/src/MGLMapAccessibilityElement.h @@ -0,0 +1,54 @@ +#import + +#import "MGLFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol MGLFeature; + +/// Unique identifier representing a single annotation in mbgl. +typedef uint32_t MGLAnnotationTag; + +/** An accessibility element representing something that appears on the map. */ +MGL_EXPORT +@interface MGLMapAccessibilityElement : UIAccessibilityElement + +@end + +/** An accessibility element representing a map annotation. */ +@interface MGLAnnotationAccessibilityElement : MGLMapAccessibilityElement + +/** The tag of the annotation represented by this element. */ +@property (nonatomic) MGLAnnotationTag tag; + +- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER; + +@end + +/** An accessibility element representing a map feature. */ +MGL_EXPORT +@interface MGLFeatureAccessibilityElement : MGLMapAccessibilityElement + +/** The feature represented by this element. */ +@property (nonatomic, strong) id feature; + +- (instancetype)initWithAccessibilityContainer:(id)container feature:(id )feature NS_DESIGNATED_INITIALIZER; + +@end + +/** An accessibility element representing a place feature. */ +MGL_EXPORT +@interface MGLPlaceFeatureAccessibilityElement : MGLFeatureAccessibilityElement +@end + +/** An accessibility element representing a road feature. */ +MGL_EXPORT +@interface MGLRoadFeatureAccessibilityElement : MGLFeatureAccessibilityElement +@end + +/** An accessibility element representing the MGLMapView at large. */ +MGL_EXPORT +@interface MGLMapViewProxyAccessibilityElement : UIAccessibilityElement +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm new file mode 100644 index 0000000000..4e5f165fbf --- /dev/null +++ b/platform/ios/src/MGLMapAccessibilityElement.mm @@ -0,0 +1,199 @@ +#import "MGLMapAccessibilityElement.h" +#import "MGLDistanceFormatter.h" +#import "MGLCompassDirectionFormatter.h" +#import "MGLFeature.h" +#import "MGLVectorSource+MGLAdditions.h" + +#import "NSBundle+MGLAdditions.h" +#import "MGLGeometry_Private.h" + +@implementation MGLMapAccessibilityElement + +- (UIAccessibilityTraits)accessibilityTraits { + return super.accessibilityTraits | UIAccessibilityTraitAdjustable; +} + +- (void)accessibilityIncrement { + [self.accessibilityContainer accessibilityIncrement]; +} + +- (void)accessibilityDecrement { + [self.accessibilityContainer accessibilityDecrement]; +} + +@end + +@implementation MGLAnnotationAccessibilityElement + +- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag { + if (self = [super initWithAccessibilityContainer:container]) { + _tag = tag; + self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint"); + } + return self; +} + +- (UIAccessibilityTraits)accessibilityTraits { + return super.accessibilityTraits | UIAccessibilityTraitButton; +} + +@end + +@implementation MGLFeatureAccessibilityElement + +- (instancetype)initWithAccessibilityContainer:(id)container feature:(id)feature { + if (self = [super initWithAccessibilityContainer:container]) { + _feature = feature; + + NSString *languageCode = [MGLVectorSource preferredMapboxStreetsLanguage]; + NSString *nameAttribute = [NSString stringWithFormat:@"name_%@", languageCode]; + NSString *name = [feature attributeForKey:nameAttribute]; + + // If a feature hasn’t been translated into the preferred language, it + // may be in the local language, which may be written in another script. + // Romanize it. + NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageCode]; + NSOrthography *orthography; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) { + orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier]; + } +#pragma clang diagnostic pop +#endif + if ([orthography.dominantScript isEqualToString:@"Latn"]) { + name = [name stringByApplyingTransform:NSStringTransformToLatin reverse:NO]; + } + + self.accessibilityLabel = name; + } + return self; +} + +- (UIAccessibilityTraits)accessibilityTraits { + return super.accessibilityTraits | UIAccessibilityTraitStaticText; +} + +@end + +@implementation MGLPlaceFeatureAccessibilityElement + +- (instancetype)initWithAccessibilityContainer:(id)container feature:(id)feature { + if (self = [super initWithAccessibilityContainer:container feature:feature]) { + NSDictionary *attributes = feature.attributes; + NSMutableArray *facts = [NSMutableArray array]; + + // Announce the kind of place or POI. + if (attributes[@"type"]) { + // FIXME: Unfortunately, these types aren’t a closed set that can be + // localized, since they’re based on OpenStreetMap tags. + NSString *type = [attributes[@"type"] stringByReplacingOccurrencesOfString:@"_" + withString:@" "]; + [facts addObject:type]; + } + // Announce the kind of airport, rail station, or mountain based on its + // Maki image name. + else if (attributes[@"maki"]) { + // TODO: Localize Maki image names. + [facts addObject:attributes[@"maki"]]; + } + + // Announce the peak’s elevation in the preferred units. + if (attributes[@"elevation_m"] ?: attributes[@"elevation_ft"]) { + NSLengthFormatter *formatter = [[NSLengthFormatter alloc] init]; + formatter.unitStyle = NSFormattingUnitStyleLong; + + NSNumber *elevationValue; + NSLengthFormatterUnit unit; + BOOL usesMetricSystem = ![[formatter.numberFormatter.locale objectForKey:NSLocaleMeasurementSystem] + isEqualToString:@"U.S."]; + if (usesMetricSystem) { + elevationValue = attributes[@"elevation_m"]; + unit = NSLengthFormatterUnitMeter; + } else { + elevationValue = attributes[@"elevation_ft"]; + unit = NSLengthFormatterUnitFoot; + } + [facts addObject:[formatter stringFromValue:elevationValue.doubleValue unit:unit]]; + } + + if (facts.count) { + NSString *separator = NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator"); + self.accessibilityValue = [facts componentsJoinedByString:separator]; + } + } + return self; +} + +@end + +@implementation MGLRoadFeatureAccessibilityElement + +- (instancetype)initWithAccessibilityContainer:(id)container feature:(id)feature { + if (self = [super initWithAccessibilityContainer:container feature:feature]) { + NSDictionary *attributes = feature.attributes; + NSMutableArray *facts = [NSMutableArray array]; + + // Announce the route number. + if (attributes[@"ref"]) { + // TODO: Decorate the route number with the network name based on the shield attribute. + NSString *ref = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ROAD_REF_A11Y_FMT", nil, nil, @"Route %@", @"String format for accessibility value for road feature; {route number}"), attributes[@"ref"]]; + [facts addObject:ref]; + } + + // Announce whether the road is a one-way road. + if ([attributes[@"oneway"] isEqualToString:@"true"]) { + [facts addObject:NSLocalizedStringWithDefaultValue(@"ROAD_ONEWAY_A11Y_VALUE", nil, nil, @"One way", @"Accessibility value indicating that a road is a one-way road")]; + } + + // Announce whether the road is a divided road. + MGLPolyline *polyline; + if ([feature isKindOfClass:[MGLMultiPolylineFeature class]]) { + [facts addObject:NSLocalizedStringWithDefaultValue(@"ROAD_DIVIDED_A11Y_VALUE", nil, nil, @"Divided road", @"Accessibility value indicating that a road is a divided road (dual carriageway)")]; + polyline = [(MGLMultiPolylineFeature *)feature polylines].firstObject; + } + + // Announce the road’s general direction. + if ([feature isKindOfClass:[MGLPolylineFeature class]]) { + polyline = (MGLPolylineFeature *)feature; + } + if (polyline) { + NSUInteger pointCount = polyline.pointCount; + if (pointCount) { + CLLocationCoordinate2D *coordinates = polyline.coordinates; + CLLocationDirection startDirection = MGLDirectionBetweenCoordinates(coordinates[pointCount - 1], coordinates[0]); + CLLocationDirection endDirection = MGLDirectionBetweenCoordinates(coordinates[0], coordinates[pointCount - 1]); + + MGLCompassDirectionFormatter *formatter = [[MGLCompassDirectionFormatter alloc] init]; + formatter.unitStyle = NSFormattingUnitStyleLong; + + NSString *startDirectionString = [formatter stringFromDirection:startDirection]; + NSString *endDirectionString = [formatter stringFromDirection:endDirection]; + NSString *directionString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ROAD_DIRECTION_A11Y_FMT", nil, nil, @"%@ to %@", @"String format for accessibility value for road feature; {starting compass direction}, {ending compass direction}"), startDirectionString, endDirectionString]; + [facts addObject:directionString]; + } + } + + if (facts.count) { + NSString *separator = NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator"); + self.accessibilityValue = [facts componentsJoinedByString:separator]; + } + } + return self; +} + +@end + +@implementation MGLMapViewProxyAccessibilityElement + +- (instancetype)initWithAccessibilityContainer:(id)container { + if (self = [super initWithAccessibilityContainer:container]) { + self.accessibilityTraits = UIAccessibilityTraitButton; + self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel]; + self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"CLOSE_CALLOUT_A11Y_HINT", nil, nil, @"Returns to the map", @"Accessibility hint for closing the selected annotation’s callout view and returning to the map"); + } + return self; +} + +@end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index ed51754b0a..c960c60c78 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -43,6 +43,7 @@ #import "MGLFoundation_Private.h" #import "MGLRendererFrontend.h" +#import "MGLVectorSource+MGLAdditions.h" #import "NSBundle+MGLAdditions.h" #import "NSDate+MGLAdditions.h" #import "NSException+MGLAdditions.h" @@ -68,6 +69,7 @@ #import "MGLAnnotationContainerView.h" #import "MGLAnnotationContainerView_Private.h" #import "MGLAttributionInfo_Private.h" +#import "MGLMapAccessibilityElement.h" #include #include @@ -139,9 +141,6 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1; const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10); -/// Unique identifier representing a single annotation in mbgl. -typedef uint32_t MGLAnnotationTag; - /// An indication that the requested annotation was not found or is nonexistent. enum { MGLAnnotationTagNotFound = UINT32_MAX }; @@ -164,38 +163,6 @@ mbgl::util::UnitBezier MGLUnitBezierForMediaTimingFunction(CAMediaTimingFunction return { p1[0], p1[1], p2[0], p2[1] }; } -@interface MGLAnnotationAccessibilityElement : UIAccessibilityElement - -@property (nonatomic) MGLAnnotationTag tag; - -- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER; - -@end - -@implementation MGLAnnotationAccessibilityElement - -- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag -{ - if (self = [super initWithAccessibilityContainer:container]) - { - _tag = tag; - self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable; - } - return self; -} - -- (void)accessibilityIncrement -{ - [self.accessibilityContainer accessibilityIncrement]; -} - -- (void)accessibilityDecrement -{ - [self.accessibilityContainer accessibilityDecrement]; -} - -@end - /// Lightweight container for metadata about an annotation, including the annotation itself. class MGLAnnotationContext { public: @@ -207,26 +174,6 @@ public: NSString *viewReuseIdentifier; }; -/** An accessibility element representing the MGLMapView at large. */ -@interface MGLMapViewProxyAccessibilityElement : UIAccessibilityElement - -@end - -@implementation MGLMapViewProxyAccessibilityElement - -- (instancetype)initWithAccessibilityContainer:(id)container -{ - if (self = [super initWithAccessibilityContainer:container]) - { - self.accessibilityTraits = UIAccessibilityTraitButton; - self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel]; - self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"CLOSE_CALLOUT_A11Y_HINT", nil, nil, @"Returns to the map", @"Accessibility hint for closing the selected annotation’s callout view and returning to the map"); - } - return self; -} - -@end - #pragma mark - Private - @interface MGLMapView () ) *_visiblePlaceFeatures; + NS_ARRAY_OF(id ) *_visibleRoadFeatures; + NS_MUTABLE_SET_OF(MGLFeatureAccessibilityElement *) *_featureAccessibilityElements; + BOOL _accessibilityValueAnnouncementIsPending; MGLReachability *_reachability; } @@ -2355,8 +2306,61 @@ public: - (NSString *)accessibilityValue { + NSMutableArray *facts = [NSMutableArray array]; + double zoomLevel = round(self.zoomLevel + 1); - return [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE", nil, nil, @"Zoom %dx\n%ld annotation(s) visible", @"Map accessibility value"), (int)zoomLevel, (long)self.accessibilityAnnotationCount]; + [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_ZOOM", nil, nil, @"Zoom %dx.", @"Map accessibility value; {zoom level}"), (int)zoomLevel]]; + + NSInteger annotationCount = self.accessibilityAnnotationCount; + if (annotationCount) { + [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_ANNOTATIONS", nil, nil, @"%ld annotation(s) visible.", @"Map accessibility value; {number of visible annotations}"), (long)self.accessibilityAnnotationCount]]; + } + + NSArray *placeFeatures = self.visiblePlaceFeatures; + if (placeFeatures.count) { + NSMutableArray *placesArray = [NSMutableArray arrayWithCapacity:placeFeatures.count]; + NSMutableSet *placesSet = [NSMutableSet setWithCapacity:placeFeatures.count]; + for (id placeFeature in placeFeatures.reverseObjectEnumerator) { + NSString *name = [placeFeature attributeForKey:@"name"]; + if (![placesSet containsObject:name]) { + [placesArray addObject:name]; + [placesSet addObject:name]; + } + if (placesArray.count >= 3) { + break; + } + } + NSString *placesString = [placesArray componentsJoinedByString:NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator")]; + [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_PLACES", nil, nil, @"Places visible: %@.", @"Map accessibility value; {list of visible places}"), placesString]]; + } + + NSArray *roadFeatures = self.visibleRoadFeatures; + if (roadFeatures.count) { + [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_ROADS", nil, nil, @"%ld road(s) visible.", @"Map accessibility value; {number of visible roads}"), roadFeatures.count]]; + } + + NSString *value = [facts componentsJoinedByString:@" "]; + return value; +} + +- (NS_ARRAY_OF(id ) *)visiblePlaceFeatures +{ + if (!_visiblePlaceFeatures) + { + NSArray *placeStyleLayerIdentifiers = [self.style.placeStyleLayers valueForKey:@"identifier"]; + _visiblePlaceFeatures = [self visibleFeaturesInRect:self.bounds inStyleLayersWithIdentifiers:[NSSet setWithArray:placeStyleLayerIdentifiers]]; + } + return _visiblePlaceFeatures; +} + +- (NS_ARRAY_OF(id ) *)visibleRoadFeatures +{ + if (!_visibleRoadFeatures) + { + NSArray *roadStyleLayerIdentifiers = [self.style.roadStyleLayers valueForKey:@"identifier"]; + _visibleRoadFeatures = [self visibleFeaturesInRect:self.bounds inStyleLayersWithIdentifiers:[NSSet setWithArray:roadStyleLayerIdentifiers]]; + } + return _visibleRoadFeatures; } - (CGRect)accessibilityFrame @@ -2390,14 +2394,9 @@ public: { if (self.calloutViewForSelectedAnnotation) { - return 2 /* selectedAnnotationCalloutView, mapViewProxyAccessibilityElement */; + return 2 /* calloutViewForSelectedAnnotation, mapViewProxyAccessibilityElement */; } - NSInteger count = self.accessibilityAnnotationCount + 2 /* compass, attributionButton */; - if (self.userLocationAnnotationView) - { - count++; - } - return count; + return !!self.userLocationAnnotationView + self.accessibilityAnnotationCount + self.visiblePlaceFeatures.count + self.visibleRoadFeatures.count + 2 /* compass, attributionButton */; } - (NSInteger)accessibilityAnnotationCount @@ -2422,67 +2421,123 @@ public: } return nil; } - std::vector visibleAnnotations = [self annotationTagsInRect:self.bounds]; - - // Ornaments - if (index == 0) + + // Compass + NSUInteger compassIndex = 0; + if (index == compassIndex) { return self.compassView; } - if ( ! self.userLocationAnnotationView) - { - index++; - } - else if (index == 1) + + // User location annotation + NSRange userLocationAnnotationRange = NSMakeRange(compassIndex + 1, !!self.userLocationAnnotationView); + if (NSLocationInRange(index, userLocationAnnotationRange)) { return self.userLocationAnnotationView; } - if (index > 0 && (NSUInteger)index == visibleAnnotations.size() + 2 /* compass, userLocationAnnotationView */) - { - return self.attributionButton; - } - - std::sort(visibleAnnotations.begin(), visibleAnnotations.end()); + CGPoint centerPoint = self.contentCenter; if (self.userTrackingMode != MGLUserTrackingModeNone) { centerPoint = self.userLocationAnnotationViewCenter; } - CLLocationCoordinate2D currentCoordinate = [self convertPoint:centerPoint toCoordinateFromView:self]; - std::sort(visibleAnnotations.begin(), visibleAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) { - CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate]; - CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate]; - CLLocationDegrees deltaA = hypot(coordinateA.latitude - currentCoordinate.latitude, - coordinateA.longitude - currentCoordinate.longitude); - CLLocationDegrees deltaB = hypot(coordinateB.latitude - currentCoordinate.latitude, - coordinateB.longitude - currentCoordinate.longitude); - return deltaA < deltaB; - }); - - NSUInteger annotationIndex = MGLAnnotationTagNotFound; - if (index >= 0 && (NSUInteger)(index - 2) < visibleAnnotations.size()) + + // Visible annotations + std::vector visibleAnnotations = [self annotationTagsInRect:self.bounds]; + NSRange visibleAnnotationRange = NSMakeRange(NSMaxRange(userLocationAnnotationRange), visibleAnnotations.size()); + if (NSLocationInRange(index, visibleAnnotationRange)) + { + std::sort(visibleAnnotations.begin(), visibleAnnotations.end()); + std::sort(visibleAnnotations.begin(), visibleAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) { + CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate]; + CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate]; + CGPoint pointA = [self convertCoordinate:coordinateA toPointToView:self]; + CGPoint pointB = [self convertCoordinate:coordinateB toPointToView:self]; + CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y); + CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y); + return deltaA < deltaB; + }); + + NSUInteger annotationIndex = index - visibleAnnotationRange.location; + MGLAnnotationTag annotationTag = visibleAnnotations[annotationIndex]; + NSAssert(annotationTag != MGLAnnotationTagNotFound, @"Can’t get accessibility element for nonexistent or invisible annotation at index %li.", (long)index); + return [self accessibilityElementForAnnotationWithTag:annotationTag]; + } + + // Visible place features + NSArray *visiblePlaceFeatures = self.visiblePlaceFeatures; + NSRange visiblePlaceFeatureRange = NSMakeRange(NSMaxRange(visibleAnnotationRange), visiblePlaceFeatures.count); + if (NSLocationInRange(index, visiblePlaceFeatureRange)) + { + visiblePlaceFeatures = [visiblePlaceFeatures sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull featureA, id _Nonnull featureB) { + CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self]; + CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self]; + CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y); + CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y); + return [@(deltaA) compare:@(deltaB)]; + }]; + + id feature = visiblePlaceFeatures[index - visiblePlaceFeatureRange.location]; + return [self accessibilityElementForPlaceFeature:feature]; + } + + // Visible road features + NSArray *visibleRoadFeatures = self.visibleRoadFeatures; + NSRange visibleRoadFeatureRange = NSMakeRange(NSMaxRange(visiblePlaceFeatureRange), visibleRoadFeatures.count); + if (NSLocationInRange(index, visibleRoadFeatureRange)) + { + visibleRoadFeatures = [visibleRoadFeatures sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull featureA, id _Nonnull featureB) { + CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self]; + CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self]; + CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y); + CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y); + return [@(deltaA) compare:@(deltaB)]; + }]; + + id feature = visibleRoadFeatures[index - visibleRoadFeatureRange.location]; + return [self accessibilityElementForRoadFeature:feature]; + } + + // Attribution button + NSUInteger attributionButtonIndex = NSMaxRange(visibleRoadFeatureRange); + if (index == attributionButtonIndex) { - annotationIndex = index - 2 /* compass, userLocationAnnotationView */; + return self.attributionButton; } - MGLAnnotationTag annotationTag = visibleAnnotations[annotationIndex]; - NSAssert(annotationTag != MGLAnnotationTagNotFound, @"Can’t get accessibility element for nonexistent or invisible annotation at index %li.", (long)index); + + NSAssert(NO, @"Index %ld not in recognized accessibility element ranges. " + @"User location annotation range: %@; visible annotation range: %@; " + @"visible place feature range: %@; visible road feature range: %@.", + (long)index, NSStringFromRange(userLocationAnnotationRange), + NSStringFromRange(visibleAnnotationRange), NSStringFromRange(visiblePlaceFeatureRange), + NSStringFromRange(visibleRoadFeatureRange)); + return nil; +} + +/** + Returns an accessibility element corresponding to a visible annotation with the given tag. + + @param annotationTag Tag of the annotation represented by the accessibility element to return. + */ +- (id)accessibilityElementForAnnotationWithTag:(MGLAnnotationTag)annotationTag +{ NSAssert(_annotationContextsByAnnotationTag.count(annotationTag), @"Missing annotation for tag %u.", annotationTag); MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); id annotation = annotationContext.annotation; - + // Let the annotation view serve as its own accessibility element. MGLAnnotationView *annotationView = annotationContext.annotationView; if (annotationView && annotationView.superview) { return annotationView; } - + // Lazily create an accessibility element for the found annotation. if ( ! annotationContext.accessibilityElement) { annotationContext.accessibilityElement = [[MGLAnnotationAccessibilityElement alloc] initWithAccessibilityContainer:self tag:annotationTag]; } - + // Update the accessibility element. MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; CGRect annotationFrame = [self frameOfImage:annotationImage.image centeredAtCoordinate:annotation.coordinate]; @@ -2493,8 +2548,7 @@ public: annotationFrame = CGRectUnion(annotationFrame, minimumFrame); CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self); annotationContext.accessibilityElement.accessibilityFrame = screenRect; - annotationContext.accessibilityElement.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint"); - + if ([annotation respondsToSelector:@selector(title)]) { annotationContext.accessibilityElement.accessibilityLabel = annotation.title; @@ -2503,10 +2557,114 @@ public: { annotationContext.accessibilityElement.accessibilityValue = annotation.subtitle; } - + return annotationContext.accessibilityElement; } +/** + Returns an accessibility element corresponding to the given place feature. + + @param feature The place feature represented by the accessibility element. + */ +- (id)accessibilityElementForPlaceFeature:(id )feature +{ + if (!_featureAccessibilityElements) + { + _featureAccessibilityElements = [NSMutableSet set]; + } + + MGLFeatureAccessibilityElement *element = [_featureAccessibilityElements objectsPassingTest:^BOOL(MGLFeatureAccessibilityElement * _Nonnull element, BOOL * _Nonnull stop) { + return element.feature.identifier && ![element.feature.identifier isEqual:@0] && [element.feature.identifier isEqual:feature.identifier]; + }].anyObject; + if (!element) + { + element = [[MGLPlaceFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature]; + } + CGPoint center = [self convertCoordinate:feature.coordinate toPointToView:self]; + CGRect annotationFrame = CGRectInset({center, CGSizeZero}, -MGLAnnotationAccessibilityElementMinimumSize.width / 2, -MGLAnnotationAccessibilityElementMinimumSize.width / 2); + CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self); + element.accessibilityFrame = screenRect; + + [_featureAccessibilityElements addObject:element]; + + return element; +} + +/** + Returns an accessibility element corresponding to the given road feature. + + @param feature The road feature represented by the accessibility element. + */ +- (id)accessibilityElementForRoadFeature:(id )feature +{ + if (!_featureAccessibilityElements) + { + _featureAccessibilityElements = [NSMutableSet set]; + } + + MGLFeatureAccessibilityElement *element = [_featureAccessibilityElements objectsPassingTest:^BOOL(MGLFeatureAccessibilityElement * _Nonnull element, BOOL * _Nonnull stop) { + return element.feature.identifier && ![element.feature.identifier isEqual:@0] && [element.feature.identifier isEqual:feature.identifier]; + }].anyObject; + if (!element) + { + element = [[MGLRoadFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature]; + } + + UIBezierPath *path; + if ([feature isKindOfClass:[MGLPointFeature class]]) + { + CGPoint center = [self convertCoordinate:feature.coordinate toPointToView:self]; + CGRect annotationFrame = CGRectInset({center, CGSizeZero}, -MGLAnnotationAccessibilityElementMinimumSize.width / 2, -MGLAnnotationAccessibilityElementMinimumSize.width / 2); + CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self); + element.accessibilityFrame = screenRect; + } + else if ([feature isKindOfClass:[MGLPolylineFeature class]]) + { + path = [self pathOfPolyline:(MGLPolyline *)feature]; + } + else if ([feature isKindOfClass:[MGLMultiPolylineFeature class]]) + { + path = [UIBezierPath bezierPath]; + for (MGLPolyline *polyline in [(MGLMultiPolylineFeature *)feature polylines]) + { + [path appendPath:[self pathOfPolyline:polyline]]; + } + } + + if (path) + { + CGPathRef strokedCGPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, MGLAnnotationAccessibilityElementMinimumSize.width, kCGLineCapButt, kCGLineJoinMiter, 0); + UIBezierPath *strokedPath = [UIBezierPath bezierPathWithCGPath:strokedCGPath]; + CGPathRelease(strokedCGPath); + UIBezierPath *screenPath = UIAccessibilityConvertPathToScreenCoordinates(strokedPath, self); + element.accessibilityPath = screenPath; + } + + [_featureAccessibilityElements addObject:element]; + + return element; +} + +- (UIBezierPath *)pathOfPolyline:(MGLPolyline *)polyline +{ + CLLocationCoordinate2D *coordinates = polyline.coordinates; + NSUInteger pointCount = polyline.pointCount; + UIBezierPath *path = [UIBezierPath bezierPath]; + for (NSUInteger i = 0; i < pointCount; i++) + { + CGPoint point = [self convertCoordinate:coordinates[i] toPointToView:self]; + if (i) + { + [path addLineToPoint:point]; + } + else + { + [path moveToPoint:point]; + } + } + return path; +} + - (NSInteger)indexOfAccessibilityElement:(id)element { if (self.calloutViewForSelectedAnnotation) @@ -2514,17 +2672,30 @@ public: return [@[self.calloutViewForSelectedAnnotation, self.mapViewProxyAccessibilityElement] indexOfObject:element]; } + + // Compass + NSUInteger compassIndex = 0; if (element == self.compassView) { - return 0; + return compassIndex; } + + // User location annotation + NSRange userLocationAnnotationRange = NSMakeRange(compassIndex + 1, !!self.userLocationAnnotationView); if (element == self.userLocationAnnotationView) { - return 1; + return userLocationAnnotationRange.location; } - + + CGPoint centerPoint = self.contentCenter; + if (self.userTrackingMode != MGLUserTrackingModeNone) + { + centerPoint = self.userLocationAnnotationViewCenter; + } + + // Visible annotations std::vector visibleAnnotations = [self annotationTagsInRect:self.bounds]; - + NSRange visibleAnnotationRange = NSMakeRange(NSMaxRange(userLocationAnnotationRange), visibleAnnotations.size()); MGLAnnotationTag tag = MGLAnnotationTagNotFound; if ([element isKindOfClass:[MGLAnnotationView class]]) { @@ -2535,22 +2706,92 @@ public: { tag = [(MGLAnnotationAccessibilityElement *)element tag]; } - else if (element == self.attributionButton) - { - return !!self.userLocationAnnotationView + visibleAnnotations.size(); + + if (tag != MGLAnnotationTagNotFound) + { + std::sort(visibleAnnotations.begin(), visibleAnnotations.end()); + std::sort(visibleAnnotations.begin(), visibleAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) { + CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate]; + CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate]; + CGPoint pointA = [self convertCoordinate:coordinateA toPointToView:self]; + CGPoint pointB = [self convertCoordinate:coordinateB toPointToView:self]; + CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y); + CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y); + return deltaA < deltaB; + }); + + auto foundElement = std::find(visibleAnnotations.begin(), visibleAnnotations.end(), tag); + if (foundElement == visibleAnnotations.end()) + { + return NSNotFound; + } + return visibleAnnotationRange.location + std::distance(visibleAnnotations.begin(), foundElement); } - else - { - return NSNotFound; + + // Visible place features + NSArray *visiblePlaceFeatures = self.visiblePlaceFeatures; + NSRange visiblePlaceFeatureRange = NSMakeRange(NSMaxRange(visibleAnnotationRange), visiblePlaceFeatures.count); + if ([element isKindOfClass:[MGLPlaceFeatureAccessibilityElement class]]) + { + visiblePlaceFeatures = [visiblePlaceFeatures sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull featureA, id _Nonnull featureB) { + CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self]; + CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self]; + CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y); + CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y); + return [@(deltaA) compare:@(deltaB)]; + }]; + + id feature = [(MGLPlaceFeatureAccessibilityElement *)element feature]; + NSUInteger featureIndex = [visiblePlaceFeatures indexOfObject:feature]; + if (featureIndex == NSNotFound) + { + featureIndex = [visiblePlaceFeatures indexOfObjectPassingTest:^BOOL (id _Nonnull visibleFeature, NSUInteger idx, BOOL * _Nonnull stop) { + return visibleFeature.identifier && ![visibleFeature.identifier isEqual:@0] && [visibleFeature.identifier isEqual:feature.identifier]; + }]; + } + if (featureIndex == NSNotFound) + { + return NSNotFound; + } + return visiblePlaceFeatureRange.location + featureIndex; } - - std::sort(visibleAnnotations.begin(), visibleAnnotations.end()); - auto foundElement = std::find(visibleAnnotations.begin(), visibleAnnotations.end(), tag); - if (foundElement == visibleAnnotations.end()) + + // Visible road features + NSArray *visibleRoadFeatures = self.visibleRoadFeatures; + NSRange visibleRoadFeatureRange = NSMakeRange(NSMaxRange(visiblePlaceFeatureRange), visibleRoadFeatures.count); + if ([element isKindOfClass:[MGLRoadFeatureAccessibilityElement class]]) + { + visibleRoadFeatures = [visibleRoadFeatures sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull featureA, id _Nonnull featureB) { + CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self]; + CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self]; + CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y); + CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y); + return [@(deltaA) compare:@(deltaB)]; + }]; + + id feature = [(MGLRoadFeatureAccessibilityElement *)element feature]; + NSUInteger featureIndex = [visibleRoadFeatures indexOfObject:feature]; + if (featureIndex == NSNotFound) + { + featureIndex = [visibleRoadFeatures indexOfObjectPassingTest:^BOOL (id _Nonnull visibleFeature, NSUInteger idx, BOOL * _Nonnull stop) { + return visibleFeature.identifier && ![visibleFeature.identifier isEqual:@0] && [visibleFeature.identifier isEqual:feature.identifier]; + }]; + } + if (featureIndex == NSNotFound) + { + return NSNotFound; + } + return visibleRoadFeatureRange.location + featureIndex; + } + + // Attribution button + NSUInteger attributionButtonIndex = NSMaxRange(visibleRoadFeatureRange); + if (element == self.attributionButton) { - return NSNotFound; + return attributionButtonIndex; } - return !!self.userLocationAnnotationView + std::distance(visibleAnnotations.begin(), foundElement) + 1 /* compass */; + + return NSNotFound; } - (MGLMapViewProxyAccessibilityElement *)mapViewProxyAccessibilityElement @@ -2581,10 +2822,11 @@ public: { centerPoint = self.userLocationAnnotationViewCenter; } - _mbglMap->setZoom(_mbglMap->getZoom() + log2(scaleFactor), mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); + double newZoom = round(self.zoomLevel) + log2(scaleFactor); + _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); [self unrotateIfNeededForGesture]; - UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.accessibilityValue); + _accessibilityValueAnnouncementIsPending = YES; } #pragma mark - Geography - @@ -5144,12 +5386,26 @@ public: { if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) { - UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + _featureAccessibilityElements = nil; + _visiblePlaceFeatures = nil; + _visibleRoadFeatures = nil; + if (_accessibilityValueAnnouncementIsPending) { + _accessibilityValueAnnouncementIsPending = NO; + [self performSelector:@selector(announceAccessibilityValue) withObject:nil afterDelay:0.1]; + } else { + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + } } [self.delegate mapView:self regionDidChangeAnimated:animated]; } } +- (void)announceAccessibilityValue +{ + UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.accessibilityValue); + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); +} + - (void)mapViewWillStartLoadingMap { if (!_mbglMap) { return; @@ -5231,6 +5487,8 @@ public: if (!_mbglMap) { return; } + + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) { -- cgit v1.2.1 From 91dabd01dfcd52dc40aa1c1d1db9d3f48f7abc97 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Thu, 9 Nov 2017 13:09:16 -0800 Subject: [ios] Silence smart invert warnings (#10425) --- platform/ios/src/MGLMapView.mm | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index c960c60c78..c002da5f18 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -387,10 +387,9 @@ public: 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; } // setup mbgl view _mbglView = new MBGLView(self); @@ -441,9 +440,6 @@ public: _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); _logoView.translatesAutoresizingMaskIntoConstraints = NO; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 - if ([_logoView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _logoView.accessibilityIgnoresInvertColors = YES; } -#endif [self addSubview:_logoView]; _logoViewConstraints = [NSMutableArray array]; @@ -452,9 +448,6 @@ public: _attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight]; _attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label"); _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint"); -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 - if ([_attributionButton respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _attributionButton.accessibilityIgnoresInvertColors = YES; } -#endif [_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside]; _attributionButton.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_attributionButton]; @@ -471,9 +464,6 @@ public: _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; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 - if ([_compassView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _compassView.accessibilityIgnoresInvertColors = YES; } -#endif [self addSubview:_compassView]; _compassViewConstraints = [NSMutableArray array]; @@ -481,9 +471,6 @@ public: // _scaleBar = [[MGLScaleBar alloc] init]; _scaleBar.translatesAutoresizingMaskIntoConstraints = NO; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 - if ([_scaleBar respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _scaleBar.accessibilityIgnoresInvertColors = YES; } -#endif [self addSubview:_scaleBar]; _scaleBarConstraints = [NSMutableArray array]; @@ -598,9 +585,7 @@ public: _glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _glView.layer.opaque = _opaque; _glView.delegate = self; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 - if ([_glView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _glView.accessibilityIgnoresInvertColors = YES; } -#endif + [_glView bindDrawable]; [self insertSubview:_glView atIndex:0]; _glView.contentMode = UIViewContentModeCenter; @@ -1960,14 +1945,14 @@ public: return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]); } - + - (CLLocationDegrees)angleBetweenPoints:(CGPoint)west east:(CGPoint)east { CGFloat slope = (west.y - east.y) / (west.x - east.x); - + CGFloat angle = atan(fabs(slope)); CLLocationDegrees degrees = MGLDegreesFromRadians(angle); - + return degrees; } -- cgit v1.2.1 From dda87bbecd44a16ddc5b4dcb7e4bbf670376b39f Mon Sep 17 00:00:00 2001 From: Fredrik Karlsson Date: Fri, 17 Nov 2017 15:17:04 +0100 Subject: [ios] update constraints when updating content inset --- platform/ios/src/MGLMapView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index ccab1fad36..b54dfc4e98 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1050,7 +1050,7 @@ public: } // Compass, logo and attribution button constraints needs to be updated. - [self setNeedsLayout]; + [self setNeedsUpdateConstraints]; } /// Returns the frame of inset content within the map view. -- cgit v1.2.1 From b2925e71acc272b4afe829149d4b87d59c1dad70 Mon Sep 17 00:00:00 2001 From: Jesse Crocker Date: Fri, 8 Sep 2017 14:01:03 -0700 Subject: [darwin] Abstract ShapeSource interface --- platform/ios/src/Mapbox.h | 1 + 1 file changed, 1 insertion(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 9b2c472cf6..effab33fd7 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -51,6 +51,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLTileSource.h" #import "MGLVectorSource.h" #import "MGLShapeSource.h" +#import "MGLAbstractShapeSource.h" #import "MGLRasterSource.h" #import "MGLImageSource.h" #import "MGLTilePyramidOfflineRegion.h" -- cgit v1.2.1 From 246417ef2435934261c8d2ab080a78572c64cbec Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 20 Nov 2017 14:04:08 -0800 Subject: [ios, macos] Implement MGLComputedShapeSource binding for CustomGeometrySource --- platform/ios/src/Mapbox.h | 1 + 1 file changed, 1 insertion(+) (limited to 'platform/ios/src') diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index effab33fd7..ce9c4965d7 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -52,6 +52,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLVectorSource.h" #import "MGLShapeSource.h" #import "MGLAbstractShapeSource.h" +#import "MGLComputedShapeSource.h" #import "MGLRasterSource.h" #import "MGLImageSource.h" #import "MGLTilePyramidOfflineRegion.h" -- cgit v1.2.1 From a2817ff5ed301f0da5817279ca7184b0c22bdf21 Mon Sep 17 00:00:00 2001 From: Andrew Kitchen Date: Mon, 27 Nov 2017 16:47:58 -0800 Subject: [darwin, macos, ios] Renames CGImage creation function to imply +1 retain count (#10570) --- platform/ios/src/UIImage+MGLAdditions.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm index 8ab1d5c259..884f92e003 100644 --- a/platform/ios/src/UIImage+MGLAdditions.mm +++ b/platform/ios/src/UIImage+MGLAdditions.mm @@ -6,7 +6,7 @@ - (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage { - CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone()); + CGImageRef image = CGImageCreateWithMGLPremultipliedImage(styleImage->getImage().clone()); if (!image) { return nil; } @@ -24,7 +24,7 @@ - (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale { - CGImageRef image = CGImageFromMGLPremultipliedImage(mbglImage.clone()); + CGImageRef image = CGImageCreateWithMGLPremultipliedImage(mbglImage.clone()); if (!image) { return nil; } -- cgit v1.2.1 From 2eec5a19803a01e21d5793706ae69ac0d886cee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 27 Nov 2017 17:25:20 +0100 Subject: [core] move HeadlessBackend extension initialization code into Impl --- platform/ios/src/MGLMapView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index b54dfc4e98..8154525334 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -6031,7 +6031,7 @@ public: [nativeView didFinishLoadingStyle]; } - mbgl::gl::ProcAddress initializeExtension(const char* name) override { + mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override { static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles")); if (!framework) { throw std::runtime_error("Failed to load OpenGL framework."); -- cgit v1.2.1 From 8f3cc3dbc4fb0f46f0e2eb00d0b644063c43a582 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Mon, 4 Dec 2017 16:40:14 -0800 Subject: [ios, macos] Rename the iOS and macOS SDKs (#10610) --- platform/ios/src/MGLMapView.mm | 2 +- platform/ios/src/MGLSDKUpdateChecker.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 8154525334..87b1a1c14e 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1975,7 +1975,7 @@ public: - (void)showAttribution:(__unused id)sender { - NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title"); + NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox Maps SDK for iOS", @"Action sheet title"); UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; diff --git a/platform/ios/src/MGLSDKUpdateChecker.mm b/platform/ios/src/MGLSDKUpdateChecker.mm index ab4ef7be86..bb61e2b595 100644 --- a/platform/ios/src/MGLSDKUpdateChecker.mm +++ b/platform/ios/src/MGLSDKUpdateChecker.mm @@ -29,7 +29,7 @@ NSString *latestVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; latestVersion = [latestVersion stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (![currentVersion isEqualToString:latestVersion]) { - NSString *updateAvailable = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"SDK_UPDATE_AVAILABLE", nil, nil, @"Mapbox iOS SDK version %@ is now available:", @"Developer-only SDK update notification; {latest version, in format x.x.x}"), latestVersion]; + NSString *updateAvailable = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"SDK_UPDATE_AVAILABLE", nil, nil, @"Mapbox Maps SDK for iOS version %@ is now available:", @"Developer-only SDK update notification; {latest version, in format x.x.x}"), latestVersion]; NSLog(@"%@ https://github.com/mapbox/mapbox-gl-native/releases/tag/ios-v%@", updateAvailable, latestVersion); } }] resume]; -- cgit v1.2.1 From c0ad7be3888452391539f87ed46dea370babc645 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Dec 2017 11:43:06 -0500 Subject: [ios] Long-press attribution button to show SDK version --- platform/ios/src/MGLMapView.mm | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 87b1a1c14e..7163302091 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -455,6 +455,9 @@ public: _attributionButtonConstraints = [NSMutableArray array]; [_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL]; + UILongPressGestureRecognizer *attributionLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showAttribution:)]; + [_attributionButton addGestureRecognizer:attributionLongPress]; + // setup compass // _compassView = [[UIImageView alloc] initWithImage:self.compassImage]; @@ -1973,13 +1976,28 @@ public: #pragma mark - Attribution - -- (void)showAttribution:(__unused id)sender +- (void)showAttribution:(id)sender { + BOOL shouldShowVersion = [sender isKindOfClass:[UILongPressGestureRecognizer class]]; + if (shouldShowVersion) + { + UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender; + if (longPress.state != UIGestureRecognizerStateBegan) + { + return; + } + } + NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox Maps SDK for iOS", @"Action sheet title"); UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - + + if (shouldShowVersion) + { + attributionController.title = [title stringByAppendingFormat:@" %@", [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"]]; + } + NSArray *attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] linkColor:nil]; for (MGLAttributionInfo *info in attributionInfos) -- cgit v1.2.1 From 3713102005b8243ade727f8084a6236954c1a1d6 Mon Sep 17 00:00:00 2001 From: Andrew Kitchen Date: Thu, 30 Nov 2017 17:13:44 -0800 Subject: [ios, macos] Adds support for specifying an ideographic font family name Adding a MGLIdeographicFontFamilyName to the containing app's Info.plist will result in CJK glyphs being rasterized on demand (#10522) --- platform/ios/src/MGLMapView.mm | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 7163302091..cf79e63632 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -188,6 +188,7 @@ public: @property (nonatomic) EAGLContext *context; @property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; + @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; @property (nonatomic, readwrite) UIImageView *compassView; @@ -196,7 +197,10 @@ public: @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; + @property (nonatomic, readwrite) MGLStyle *style; +@property (nonatomic, readonly) NSString *ideographicFontFamilyName; + @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @property (nonatomic) UITapGestureRecognizer *twoFingerTap; @@ -205,11 +209,14 @@ public: @property (nonatomic) UIRotationGestureRecognizer *rotate; @property (nonatomic) UILongPressGestureRecognizer *quickZoom; @property (nonatomic) UIPanGestureRecognizer *twoFingerDrag; + /// Mapping from reusable identifiers to annotation images. @property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier; + /// Currently shown popover representing the selected annotation. @property (nonatomic) UIView *calloutViewForSelectedAnnotation; @property (nonatomic) MGLUserLocationAnnotationView *userLocationAnnotationView; + /// Indicates how thoroughly the map view is tracking the user location. @property (nonatomic) MGLUserTrackingState userTrackingState; @property (nonatomic) CLLocationManager *locationManager; @@ -403,8 +410,9 @@ public: mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; _mbglThreadPool = mbgl::sharedThreadPool(); - - auto renderer = std::make_unique(*_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique); + NSString *fontFamilyName = self.ideographicFontFamilyName; + + auto renderer = std::make_unique(*_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique, mbgl::optional(), fontFamilyName ? std::string([fontFamilyName UTF8String]) : mbgl::optional()); _rendererFrontend = std::make_unique(std::move(renderer), self, *_mbglView); _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); @@ -3474,6 +3482,12 @@ public: [self.style removeStyleClass:styleClass]; } +#pragma mark Ideographic Font Info + +- (NSString *)ideographicFontFamilyName { + return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLIdeographicFontFamilyName"]; +} + #pragma mark - Annotations - - (nullable NS_ARRAY_OF(id ) *)annotations -- cgit v1.2.1 From b2f06677a787fe7b9b08608e5a55aaedbe50ed3a Mon Sep 17 00:00:00 2001 From: Andrew Kitchen Date: Mon, 4 Dec 2017 13:29:16 -0800 Subject: [darwin, ios, macos] Introduces an MGLRendererConfiguration class Instructions for enabling client-side rendering of CJK glyphs live in this header, and this class provides the rest of the values needed for instantiating the renderer on iOS and macOS. --- platform/ios/src/MGLMapView.mm | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index cf79e63632..c60e56f808 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -43,6 +43,7 @@ #import "MGLOfflineStorage_Private.h" #import "MGLFoundation_Private.h" #import "MGLRendererFrontend.h" +#import "MGLRendererConfiguration.h" #import "MGLVectorSource+MGLAdditions.h" #import "NSBundle+MGLAdditions.h" @@ -199,7 +200,6 @@ public: @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; @property (nonatomic, readwrite) MGLStyle *style; -@property (nonatomic, readonly) NSString *ideographicFontFamilyName; @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @@ -324,7 +324,7 @@ public: + (void)initialize { - if (self == [MGLMapView self]) + if (self == [MGLMapView class]) { [MGLSDKUpdateChecker checkForUpdates]; } @@ -407,14 +407,12 @@ public: [[NSFileManager defaultManager] removeItemAtPath:fileCachePath error:NULL]; // setup mbgl map - mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource; - const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; + MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; _mbglThreadPool = mbgl::sharedThreadPool(); - NSString *fontFamilyName = self.ideographicFontFamilyName; - auto renderer = std::make_unique(*_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique, mbgl::optional(), fontFamilyName ? std::string([fontFamilyName UTF8String]) : mbgl::optional()); + auto renderer = std::make_unique(*_mbglView, config.scaleFactor, *config.fileSource, *_mbglThreadPool, config.contextMode, config.cacheDir, config.localFontFamilyName); _rendererFrontend = std::make_unique(std::move(renderer), self, *_mbglView); - _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); + _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, config.scaleFactor, *[config fileSource], *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default); // start paused if in IB if (_isTargetingInterfaceBuilder || background) { @@ -3482,12 +3480,6 @@ public: [self.style removeStyleClass:styleClass]; } -#pragma mark Ideographic Font Info - -- (NSString *)ideographicFontFamilyName { - return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLIdeographicFontFamilyName"]; -} - #pragma mark - Annotations - - (nullable NS_ARRAY_OF(id ) *)annotations -- cgit v1.2.1 From 637892cd6f0f8953b787b9d727bdf82ed3813c96 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 2 Jan 2018 15:36:37 -0500 Subject: [ios, macos] Move private categories into explicitly private headers Instead of creating `MGLAdditions` categories/files for our own classes, this standardizes on the `Private` convention that we had been using elsewhere. This also fixes build errors where the `MGLAdditions` categories weren't importing their parents. --- platform/ios/src/MGLMapAccessibilityElement.mm | 5 +++-- platform/ios/src/MGLMapView.mm | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'platform/ios/src') diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm index 4e5f165fbf..1a2953b0bb 100644 --- a/platform/ios/src/MGLMapAccessibilityElement.mm +++ b/platform/ios/src/MGLMapAccessibilityElement.mm @@ -2,10 +2,11 @@ #import "MGLDistanceFormatter.h" #import "MGLCompassDirectionFormatter.h" #import "MGLFeature.h" -#import "MGLVectorSource+MGLAdditions.h" -#import "NSBundle+MGLAdditions.h" #import "MGLGeometry_Private.h" +#import "MGLVectorSource_Private.h" + +#import "NSBundle+MGLAdditions.h" @implementation MGLMapAccessibilityElement diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index c60e56f808..8dec9e520d 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -41,11 +41,11 @@ #import "MGLGeometry_Private.h" #import "MGLMultiPoint_Private.h" #import "MGLOfflineStorage_Private.h" +#import "MGLVectorSource_Private.h" #import "MGLFoundation_Private.h" #import "MGLRendererFrontend.h" #import "MGLRendererConfiguration.h" -#import "MGLVectorSource+MGLAdditions.h" #import "NSBundle+MGLAdditions.h" #import "NSDate+MGLAdditions.h" #import "NSException+MGLAdditions.h" -- cgit v1.2.1