diff --git a/include/mbgl/ios/MGLAnnotationImage.h b/include/mbgl/ios/MGLAnnotationImage.h index 44d17087bc6..70bf8b75991 100644 --- a/include/mbgl/ios/MGLAnnotationImage.h +++ b/include/mbgl/ios/MGLAnnotationImage.h @@ -27,6 +27,11 @@ NS_ASSUME_NONNULL_BEGIN * If you define distinctly different types of annotations (with distinctly different annotation images to go with them), you can differentiate between the annotation types by specifying different reuse identifiers for each one. */ @property (nonatomic, readonly) NSString *reuseIdentifier; +/** A Boolean value indicating whether the annotation is enabled. +* +* The default value of this property is `YES`. If the value of this property is `NO`, the annotation image ignores touch events and cannot be selected. */ +@property (nonatomic, getter=isEnabled) BOOL enabled; + @end NS_ASSUME_NONNULL_END diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index bb35a079101..d05d142a52c 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -357,6 +357,15 @@ - (MGLAnnotationImage *)mapView:(MGLMapView * __nonnull)mapView imageForAnnotati NSString *title = [(MGLPointAnnotation *)annotation title]; NSString *lastTwoCharacters = [title substringFromIndex:title.length - 2]; + UIColor *color; + + // make every tenth annotation blue + if ([lastTwoCharacters hasSuffix:@"0"]) { + color = [UIColor blueColor]; + } else { + color = [UIColor redColor]; + } + MGLAnnotationImage *image = [mapView dequeueReusableAnnotationImageWithIdentifier:lastTwoCharacters]; if ( ! image) @@ -367,7 +376,7 @@ - (MGLAnnotationImage *)mapView:(MGLMapView * __nonnull)mapView imageForAnnotati CGContextRef ctx = UIGraphicsGetCurrentContext(); - CGContextSetFillColorWithColor(ctx, [[[UIColor redColor] colorWithAlphaComponent:0.75] CGColor]); + CGContextSetFillColorWithColor(ctx, [[color colorWithAlphaComponent:0.75] CGColor]); CGContextFillRect(ctx, rect); CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]); @@ -385,6 +394,9 @@ - (MGLAnnotationImage *)mapView:(MGLMapView * __nonnull)mapView imageForAnnotati image = [MGLAnnotationImage annotationImageWithImage:UIGraphicsGetImageFromCurrentImageContext() reuseIdentifier:lastTwoCharacters]; + // don't allow touches on blue annotations + if ([color isEqual:[UIColor blueColor]]) image.enabled = NO; + UIGraphicsEndImageContext(); } diff --git a/platform/ios/MGLAnnotationImage.m b/platform/ios/MGLAnnotationImage.m index e5052bc03ab..cd211c52e61 100644 --- a/platform/ios/MGLAnnotationImage.m +++ b/platform/ios/MGLAnnotationImage.m @@ -22,6 +22,7 @@ - (instancetype)initWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuse { _image = image; _reuseIdentifier = [reuseIdentifier copy]; + _enabled = YES; } return self; diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 3906dd84b4a..3b86c7b9878 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -21,6 +21,7 @@ #include #include #include +#include #import "Mapbox.h" @@ -37,6 +38,7 @@ #import #import +#import class MBGLView; @@ -52,6 +54,7 @@ const CGFloat MGLMinimumPitch = 0; const CGFloat MGLMaximumPitch = 60; const CLLocationDegrees MGLAngularFieldOfView = M_PI / 6.; +const std::string spritePrefix = "com.mapbox.sprites."; NSString *const MGLAnnotationIDKey = @"MGLAnnotationIDKey"; NSString *const MGLAnnotationSymbolKey = @"MGLAnnotationSymbolKey"; @@ -1158,44 +1161,78 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap if (nearbyAnnotations.size()) { - // there is at least one nearby annotation; select one - // - // first, sort for comparison and iteration - std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end()); + // pare down nearby annotations to only enabled ones + NSEnumerator *metadataEnumerator = [self.annotationMetadataByAnnotation objectEnumerator]; + NSString *prefix = [NSString stringWithUTF8String:spritePrefix.c_str()]; + std::unordered_set disabledAnnotationIDs; - if (nearbyAnnotations == self.annotationsNearbyLastTap) + while (NSDictionary *metadata = [metadataEnumerator nextObject]) { - // the selection candidates haven't changed; cycle through them - if (self.selectedAnnotation && - [[[self.annotationMetadataByAnnotation objectForKey:self.selectedAnnotation] - objectForKey:MGLAnnotationIDKey] unsignedIntValue] == self.annotationsNearbyLastTap.back()) + // This iterates ALL annotations' metadata dictionaries, using their + // reuse identifiers to get at the stored annotation image objects, + // which we can then query for enabled status. + NSString *reuseIdentifier = [metadata[MGLAnnotationSymbolKey] stringByReplacingOccurrencesOfString:prefix + withString:@"" + options:NSAnchoredSearch + range:NSMakeRange(0, prefix.length)]; + + MGLAnnotationImage *annotationImage = self.annotationImages[reuseIdentifier]; + + if (annotationImage.isEnabled == NO) { - // the selected annotation is the last in the set; cycle back to the first - // note: this could be the selected annotation if only one in set - newSelectedAnnotationID = self.annotationsNearbyLastTap.front(); + disabledAnnotationIDs.emplace([metadata[MGLAnnotationIDKey] unsignedIntValue]); } - else if (self.selectedAnnotation) + } + + if (disabledAnnotationIDs.size()) + { + // Clear out any nearby annotations that are in our set of + // disabled annotations. + mbgl::util::erase_if(nearbyAnnotations, [&](const uint32_t annotationID) { + return disabledAnnotationIDs.count(annotationID) != 0; + }); + } + + // only proceed if there are still annotations + if (nearbyAnnotations.size() > 0) + { + // first, sort for comparison and iteration + std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end()); + + if (nearbyAnnotations == self.annotationsNearbyLastTap) { - // otherwise increment the selection through the candidates - uint32_t currentID = [[[self.annotationMetadataByAnnotation objectForKey:self.selectedAnnotation] objectForKey:MGLAnnotationIDKey] unsignedIntValue]; - auto result = std::find(self.annotationsNearbyLastTap.begin(), self.annotationsNearbyLastTap.end(), currentID); - auto distance = std::distance(self.annotationsNearbyLastTap.begin(), result); - newSelectedAnnotationID = self.annotationsNearbyLastTap[distance + 1]; + // the selection candidates haven't changed; cycle through them + if (self.selectedAnnotation && + [[[self.annotationMetadataByAnnotation objectForKey:self.selectedAnnotation] + objectForKey:MGLAnnotationIDKey] unsignedIntValue] == self.annotationsNearbyLastTap.back()) + { + // the selected annotation is the last in the set; cycle back to the first + // note: this could be the selected annotation if only one in set + newSelectedAnnotationID = self.annotationsNearbyLastTap.front(); + } + else if (self.selectedAnnotation) + { + // otherwise increment the selection through the candidates + uint32_t currentID = [[[self.annotationMetadataByAnnotation objectForKey:self.selectedAnnotation] objectForKey:MGLAnnotationIDKey] unsignedIntValue]; + auto result = std::find(self.annotationsNearbyLastTap.begin(), self.annotationsNearbyLastTap.end(), currentID); + auto distance = std::distance(self.annotationsNearbyLastTap.begin(), result); + newSelectedAnnotationID = self.annotationsNearbyLastTap[distance + 1]; + } + else + { + // no current selection; select the first one + newSelectedAnnotationID = self.annotationsNearbyLastTap.front(); + } } else { - // no current selection; select the first one + // start tracking a new set of nearby annotations + self.annotationsNearbyLastTap = nearbyAnnotations; + + // select the first one newSelectedAnnotationID = self.annotationsNearbyLastTap.front(); } } - else - { - // start tracking a new set of nearby annotations - self.annotationsNearbyLastTap = nearbyAnnotations; - - // select the first one - newSelectedAnnotationID = self.annotationsNearbyLastTap.front(); - } } else {