diff --git a/include/mbgl/ios/MGLAnnotationImage.h b/include/mbgl/ios/MGLAnnotationImage.h index 44d17087b..70bf8b759 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 bb35a0791..d05d142a5 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -357,6 +357,15 @@ static NSUInteger const kStyleVersion = 8; 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 @@ static NSUInteger const kStyleVersion = 8; 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 @@ static NSUInteger const kStyleVersion = 8; 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 e5052bc03..cd211c52e 100644 --- a/platform/ios/MGLAnnotationImage.m +++ b/platform/ios/MGLAnnotationImage.m @@ -22,6 +22,7 @@ { _image = image; _reuseIdentifier = [reuseIdentifier copy]; + _enabled = YES; } return self; diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 3906dd84b..3b86c7b98 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 MGLMinimumZoom = 3; 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 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) 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 {