diff options
Diffstat (limited to 'platform/ios/src/MGLMapView.mm')
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 258 |
1 files changed, 148 insertions, 110 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index fe3f12b152..2476ec3e97 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -221,10 +221,8 @@ public: @interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate, CLLocationManagerDelegate, - UIActionSheetDelegate, SMCalloutViewDelegate, MGLCalloutViewDelegate, - UIAlertViewDelegate, MGLMultiPointDelegate, MGLAnnotationImageDelegate> @@ -237,7 +235,6 @@ public: @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; -@property (nonatomic) UIActionSheet *attributionSheet; @property (nonatomic, readwrite) MGLStyle *style; @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @@ -315,8 +312,6 @@ public: MGLCompassDirectionFormatter *_accessibilityCompassFormatter; - NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfos; - MGLReachability *_reachability; } @@ -890,11 +885,6 @@ public: _mbglMap->setSize([self size]); } - if (self.attributionSheet.visible) - { - [self.attributionSheet dismissWithClickedButtonIndex:self.attributionSheet.cancelButtonIndex animated:YES]; - } - if (self.compassView.alpha) { [self updateHeadingForDeviceOrientation]; @@ -1456,7 +1446,9 @@ public: } else { - nextElement = _annotationContextsByAnnotationTag[_selectedAnnotationTag].accessibilityElement; + if (_selectedAnnotationTag != MGLAnnotationTagNotFound) { + nextElement = _annotationContextsByAnnotationTag.at(_selectedAnnotationTag).accessibilityElement; + } } [self deselectAnnotation:self.selectedAnnotation animated:YES]; UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement); @@ -1875,82 +1867,108 @@ public: - (void)showAttribution { - self.attributionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title") - delegate:self - cancelButtonTitle:NSLocalizedStringWithDefaultValue(@"CANCEL", nil, nil, @"Cancel", @"") - destructiveButtonTitle:nil - otherButtonTitles:nil]; - - _attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] linkColor:nil]; - for (MGLAttributionInfo *info in _attributionInfos) + NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title"); + UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title + message:nil + preferredStyle:UIAlertControllerStyleActionSheet]; + + NSArray *attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] + linkColor:nil]; + for (MGLAttributionInfo *info in attributionInfos) { NSString *title = [info.title.string mgl_titleCasedStringWithLocale:[NSLocale currentLocale]]; - [self.attributionSheet addButtonWithTitle:title]; - } - - [self.attributionSheet addButtonWithTitle:NSLocalizedStringWithDefaultValue(@"TELEMETRY_NAME", nil, nil, @"Mapbox Telemetry", @"Action in attribution sheet")]; - - [self.attributionSheet showFromRect:self.attributionButton.frame inView:self animated:YES]; -} - -- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex -{ - if (buttonIndex == actionSheet.numberOfButtons - 1) - { - NSString *message; - NSString *participate; - NSString *optOut; - - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"]) - { - message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_MSG", nil, nil, @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); - participate = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_ON", nil, nil, @"Keep Participating", @"Telemetry prompt button"); - optOut = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_OFF", nil, nil, @"Stop Participating", @"Telemetry prompt button"); - } - else - { - message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_MSG", nil, nil, @"You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); - participate = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_ON", nil, nil, @"Participate", @"Telemetry prompt button"); - optOut = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_OFF", nil, nil, @"Don’t Participate", @"Telemetry prompt button"); - } - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"TELEMETRY_TITLE", nil, nil, @"Make Mapbox Maps Better", @"Telemetry prompt title") - message:message - delegate:self - cancelButtonTitle:participate - otherButtonTitles:NSLocalizedStringWithDefaultValue(@"TELEMETRY_MORE", nil, nil, @"Tell Me More", @"Telemetry prompt button"), optOut, nil]; - [alert show]; - } - else if (buttonIndex > 0) - { - MGLAttributionInfo *info = _attributionInfos[buttonIndex + actionSheet.firstOtherButtonIndex]; - NSURL *url = info.URL; - if (url) - { - if (info.feedbackLink) + UIAlertAction *action = [UIAlertAction actionWithTitle:title + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + NSURL *url = info.URL; + if (url) { - url = [info feedbackURLAtCenterCoordinate:self.centerCoordinate zoomLevel:self.zoomLevel]; + if (info.feedbackLink) + { + url = [info feedbackURLAtCenterCoordinate:self.centerCoordinate + zoomLevel:self.zoomLevel]; + } + [[UIApplication sharedApplication] openURL:url]; } - [[UIApplication sharedApplication] openURL:url]; - } + }]; + [attributionController addAction:action]; } + + NSString *telemetryTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_NAME", nil, nil, @"Mapbox Telemetry", @"Action in attribution sheet"); + UIAlertAction *telemetryAction = [UIAlertAction actionWithTitle:telemetryTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self presentTelemetryAlertController]; + }]; + [attributionController addAction:telemetryAction]; + + NSString *cancelTitle = NSLocalizedStringWithDefaultValue(@"CANCEL", nil, nil, @"Cancel", @""); + UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelTitle + style:UIAlertActionStyleCancel + handler:NULL]; + [attributionController addAction:cancelAction]; + + attributionController.popoverPresentationController.sourceView = self; + attributionController.popoverPresentationController.sourceRect = self.attributionButton.frame; + + UIViewController *viewController = self.window.rootViewController; + if ([viewController isKindOfClass:[UINavigationController class]]) { + viewController = [(UINavigationController *)viewController viewControllers].firstObject; + } + [viewController presentViewController:attributionController + animated:YES + completion:NULL]; } -- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex +- (void)presentTelemetryAlertController { - if (buttonIndex == alertView.cancelButtonIndex) + NSString *title = NSLocalizedStringWithDefaultValue(@"TELEMETRY_TITLE", nil, nil, @"Make Mapbox Maps Better", @"Telemetry prompt title"); + NSString *message; + NSString *participateTitle; + NSString *declineTitle; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MGLMapboxMetricsEnabled"]; + message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_MSG", nil, nil, @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); + participateTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_ON", nil, nil, @"Keep Participating", @"Telemetry prompt button"); + declineTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_OFF", nil, nil, @"Stop Participating", @"Telemetry prompt button"); } - else if (buttonIndex == alertView.firstOtherButtonIndex) + else { + message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_MSG", nil, nil, @"You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); + participateTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_ON", nil, nil, @"Participate", @"Telemetry prompt button"); + declineTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_OFF", nil, nil, @"Don’t Participate", @"Telemetry prompt button"); + } + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + NSString *moreTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_MORE", nil, nil, @"Tell Me More", @"Telemetry prompt button"); + UIAlertAction *moreAction = [UIAlertAction actionWithTitle:moreTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { [[UIApplication sharedApplication] openURL: [NSURL URLWithString:@"https://www.mapbox.com/telemetry/"]]; - } - else if (buttonIndex == alertView.firstOtherButtonIndex + 1) - { + }]; + [alertController addAction:moreAction]; + + UIAlertAction *declineAction = [UIAlertAction actionWithTitle:declineTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"MGLMapboxMetricsEnabled"]; - } + }]; + [alertController addAction:declineAction]; + + UIAlertAction *participateAction = [UIAlertAction actionWithTitle:participateTitle + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * _Nonnull action) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MGLMapboxMetricsEnabled"]; + }]; + [alertController addAction:participateAction]; + + [self.window.rootViewController presentViewController:alertController + animated:YES + completion:NULL]; } #pragma mark - Properties - @@ -1980,19 +1998,21 @@ public: { const mbgl::Point<double> point = MGLPointFromLocationCoordinate2D(annotation.coordinate); - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - if (annotationContext.annotationView) - { - // Redundantly move the associated annotation view outside the scope of the animation-less transaction block in -updateAnnotationViews. - annotationContext.annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]; - } + if (annotationTag != MGLAnnotationTagNotFound) { + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + if (annotationContext.annotationView) + { + // Redundantly move the associated annotation view outside the scope of the animation-less transaction block in -updateAnnotationViews. + annotationContext.annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]; + } - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - NSString *symbolName = annotationImage.styleIconIdentifier; + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + NSString *symbolName = annotationImage.styleIconIdentifier; - // Update the annotation’s backing geometry to match the annotation model object. Any associated annotation view is also moved by side effect. However, -updateAnnotationViews disables the view’s animation actions, because it can’t distinguish between moves due to the viewport changing and moves due to the annotation’s coordinate changing. - _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String }); - [self updateCalloutView]; + // Update the annotation’s backing geometry to match the annotation model object. Any associated annotation view is also moved by side effect. However, -updateAnnotationViews disables the view’s animation actions, because it can’t distinguish between moves due to the viewport changing and moves due to the annotation’s coordinate changing. + _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String }); + [self updateCalloutView]; + } } } else if ([keyPath isEqualToString:@"coordinates"] && [object isKindOfClass:[MGLMultiPoint class]]) @@ -3068,12 +3088,18 @@ public: for (auto const& annotationTag: annotationTags) { - if (!_annotationContextsByAnnotationTag.count(annotationTag)) + if (!_annotationContextsByAnnotationTag.count(annotationTag) || + annotationTag == MGLAnnotationTagNotFound) { continue; } + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - [annotations addObject:annotationContext.annotation]; + NSAssert(annotationContext.annotation, @"Missing annotation for tag %u.", annotationTag); + if (annotationContext.annotation) + { + [annotations addObject:annotationContext.annotation]; + } } return [annotations copy]; @@ -3085,12 +3111,12 @@ public: /// Returns the annotation assigned the given tag. Cheap. - (id <MGLAnnotation>)annotationWithTag:(MGLAnnotationTag)tag { - if ( ! _annotationContextsByAnnotationTag.count(tag)) - { + if ( ! _annotationContextsByAnnotationTag.count(tag) || + tag == MGLAnnotationTagNotFound) { return nil; } - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag[tag]; + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); return annotationContext.annotation; } @@ -3560,8 +3586,12 @@ public: { id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag]; NSAssert(annotation, @"Unknown annotation found nearby tap"); + if ( ! annotation) + { + return true; + } - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag]; + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); CGRect annotationRect; MGLAnnotationView *annotationView = annotationContext.annotationView; @@ -3684,10 +3714,12 @@ public: { return self.userLocation; } - if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag)) - { + + if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) || + _selectedAnnotationTag == MGLAnnotationTagNotFound) { return nil; } + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(_selectedAnnotationTag); return annotationContext.annotation; } @@ -3753,19 +3785,16 @@ public: MGLAnnotationView *annotationView = nil; if (annotation != self.userLocation) - { - 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]; + if (annotationTag != MGLAnnotationTagNotFound) { + 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]; + } } } @@ -3787,7 +3816,14 @@ public: UIView <MGLCalloutView> *calloutView; if ([self.delegate respondsToSelector:@selector(mapView:calloutViewForAnnotation:)]) { - calloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation]; + id providedCalloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation]; + if (providedCalloutView) { + if (![providedCalloutView isKindOfClass:[UIView class]]) { + [NSException raise:NSInvalidArgumentException format:@"Callout view must be a kind of UIView"]; + } + NSAssert([providedCalloutView conformsToProtocol:@protocol(MGLCalloutView)], @"callout view must conform to MGLCalloutView"); + calloutView = providedCalloutView; + } } if (!calloutView) { @@ -3868,8 +3904,6 @@ public: /// and is appropriate for positioning a popover. - (CGRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annotationTag { - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag]; - id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag]; if ( ! annotation) { @@ -5048,8 +5082,12 @@ public: if (isAnchoredToAnnotation) { MGLAnnotationTag tag = [self annotationTagForAnnotation:annotation]; - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); - MGLAnnotationView *annotationView = annotationContext.annotationView; + MGLAnnotationView *annotationView = nil; + + if (tag != MGLAnnotationTagNotFound) { + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); + annotationView = annotationContext.annotationView; + } CGRect rect = [self positioningRectForCalloutForAnnotationWithTag:tag]; |