diff options
Diffstat (limited to 'platform/ios/src/MGLMapView.mm')
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 152 |
1 files changed, 117 insertions, 35 deletions
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index cde877407b..a036e6d812 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -88,6 +88,10 @@ const CLLocationDirection MGLToleranceForSnappingToNorth = 7; /// Reuse identifier and file name of the default point annotation image. static NSString * const MGLDefaultStyleMarkerSymbolName = @"default_marker"; +/// Reuse identifier and file name of the invisible point annotation image used +/// by annotations that are visually backed by MGLAnnotationView objects +static NSString * const MGLInvisibleStyleMarkerSymbolName = @"invisible_marker"; + /// Prefix that denotes a sprite installed by MGLMapView, to avoid collisions /// with style-defined sprites. NSString *const MGLAnnotationSpritePrefix = @"com.mapbox.sprites."; @@ -255,8 +259,9 @@ public: NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *_annotationViewReuseQueueByIdentifier; BOOL _userLocationAnnotationIsSelected; - /// Size of the rectangle formed by unioning the maximum slop area around every annotation image. - CGSize _unionedAnnotationImageSize; + /// Size of the rectangle formed by unioning the maximum slop area around every annotation image and annotation image view. + CGSize _unionedAnnotationRepresentationSize; + CGSize _largestAnnotationViewSize; std::vector<MGLAnnotationTag> _annotationsNearbyLastTap; CGPoint _initialImplicitCalloutViewOffset; NSDate *_userLocationAnimationCompletionDate; @@ -282,8 +287,6 @@ public: BOOL _delegateHasLineWidthsForShapeAnnotations; MGLCompassDirectionFormatter *_accessibilityCompassFormatter; - - CGSize _largestAnnotationViewSize; } #pragma mark - Setup & Teardown - @@ -1413,13 +1416,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) } return; } - - MGLAnnotationView *hitAnnotationView = [self annotationViewAtPoint:tapPoint]; - if (hitAnnotationView) - { - [self selectAnnotation:hitAnnotationView.annotation animated:YES]; - return; - } MGLAnnotationTag hitAnnotationTag = [self annotationTagAtPoint:tapPoint persistingResults:YES]; if (hitAnnotationTag != MGLAnnotationTagNotFound) @@ -2866,8 +2862,17 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) if (annotationView) { annotationViewsForAnnotation[annotationValue] = annotationView; + annotationView.annotation = annotation; annotationView.center = [self convertCoordinate:annotation.coordinate toPointToView:self]; [newAnnotationViews addObject:annotationView]; + + MGLAnnotationImage *annotationImage = self.invisibleAnnotationImage; + symbolName = annotationImage.styleIconIdentifier; + annotationImagesForAnnotation[annotationValue] = annotationImage; + if ( ! self.annotationImagesByIdentifier[annotationImage.reuseIdentifier]) + { + [self installAnnotationImage:annotationImage]; + } } } @@ -2904,22 +2909,21 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) MGLAnnotationTag annotationTag = _mbglMap->addAnnotation(mbgl::SymbolAnnotation { MGLPointFromLocationCoordinate2D(annotation.coordinate), - symbolName.UTF8String ?: "" + symbolName.UTF8String }); MGLAnnotationContext context; context.annotation = annotation; MGLAnnotationImage *annotationImage = annotationImagesForAnnotation[annotationValue]; + context.imageReuseIdentifier = annotationImage.reuseIdentifier; - if (annotationImage) { - context.imageReuseIdentifier = annotationImage.reuseIdentifier; - } if (annotationView) { context.annotationView = annotationView; context.viewReuseIdentifier = annotationView.reuseIdentifier; } _annotationContextsByAnnotationTag[annotationTag] = context; + if ([annotation isKindOfClass:[NSObject class]]) { NSAssert(![annotation isKindOfClass:[MGLMultiPoint class]], @"Point annotation should not be MGLMultiPoint."); [(NSObject *)annotation addObserver:self forKeyPath:@"coordinate" options:0 context:(void *)(NSUInteger)annotationTag]; @@ -2969,6 +2973,23 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) return annotationImage; } +- (MGLAnnotationImage *)invisibleAnnotationImage +{ + MGLAnnotationImage *annotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLInvisibleStyleMarkerSymbolName]; + + if (!annotationImage) + { + UIGraphicsBeginImageContext(CGSizeMake(1, 1)); + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + annotationImage = [MGLAnnotationImage annotationImageWithImage:image + reuseIdentifier:MGLInvisibleStyleMarkerSymbolName]; + annotationImage.styleIconIdentifier = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; + } + + return annotationImage; +} + - (MGLAnnotationView *)annotationViewForAnnotation:(id<MGLAnnotation>)annotation { MGLAnnotationView *annotationView = [self.delegate mapView:self viewForAnnotation:annotation]; @@ -2977,12 +2998,26 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) { annotationView.annotation = annotation; CGRect bounds = UIEdgeInsetsInsetRect({ CGPointZero, annotationView.frame.size }, annotationView.alignmentRectInsets); - _largestAnnotationViewSize = CGSizeMake(bounds.size.width / 2.0, bounds.size.height / 2.0); + + _largestAnnotationViewSize = CGSizeMake(MAX(_largestAnnotationViewSize.width, CGRectGetWidth(bounds)), + MAX(_largestAnnotationViewSize.height, CGRectGetHeight(bounds))); + + _unionedAnnotationRepresentationSize = CGSizeMake(MAX(_unionedAnnotationRepresentationSize.width, _largestAnnotationViewSize.width), + MAX(_unionedAnnotationRepresentationSize.height, _largestAnnotationViewSize.height)); } return annotationView; } +- (nullable MGLAnnotationView *)viewForAnnotation:(id<MGLAnnotation>)annotation +{ + MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation]; + if (annotationTag == MGLAnnotationTagNotFound) return nil; + + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + return annotationContext.annotationView; +} + - (double)alphaForShapeAnnotation:(MGLShape *)annotation { if (_delegateHasAlphasForShapeAnnotations) @@ -3050,8 +3085,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) // within this image. Union this slop area with any existing slop areas. CGRect bounds = UIEdgeInsetsInsetRect({ CGPointZero, annotationImage.image.size }, annotationImage.image.alignmentRectInsets); - _unionedAnnotationImageSize = CGSizeMake(MAX(_unionedAnnotationImageSize.width, bounds.size.width), - MAX(_unionedAnnotationImageSize.height, bounds.size.height)); + _unionedAnnotationRepresentationSize = CGSizeMake(MAX(_unionedAnnotationRepresentationSize.width, bounds.size.width), + MAX(_unionedAnnotationRepresentationSize.height, bounds.size.height)); } - (void)removeAnnotation:(id <MGLAnnotation>)annotation @@ -3083,6 +3118,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); MGLAnnotationView *annotationView = annotationContext.annotationView; + annotationView.annotation = nil; [annotationView removeFromSuperview]; if (annotationTag == _selectedAnnotationTag) @@ -3188,10 +3224,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) { // Look for any annotation near the tap. An annotation is “near” if the // distance between its center and the tap is less than the maximum height - // or width of an installed annotation image. + // or width of an installed annotation image or annotation view. CGRect queryRect = CGRectInset({ point, CGSizeZero }, - -_unionedAnnotationImageSize.width, - -_unionedAnnotationImageSize.height); + -_unionedAnnotationRepresentationSize.width, + -_unionedAnnotationRepresentationSize.height); queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); std::vector<MGLAnnotationTag> nearbyAnnotations = [self annotationTagsInRect:queryRect]; @@ -3203,10 +3239,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); - MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; - UIImage *fallbackImage = fallbackAnnotationImage.image; - - // Filter out any annotation whose image is unselectable or for which + // 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) @@ -3215,17 +3248,36 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) NSAssert(annotation, @"Unknown annotation found nearby tap"); MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag]; + CGRect annotationRect; - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - if ( ! annotationImage.enabled) + MGLAnnotationView *annotationView = annotationContext.annotationView; + if (annotationView) + { + 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 { - return true; + 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]; } // Filter out the annotation if the fattened finger didn’t land // within the image’s alignment rect. - CGRect annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; - return !!!CGRectIntersectsRect(annotationRect, hitRect); }); @@ -3382,17 +3434,22 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) // 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) { MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - MGLAnnotationView *annotationView = annotationContext.annotationView; - if (annotationView) + 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.selected = YES; } } @@ -3474,6 +3531,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) { [self.delegate mapView:self didSelectAnnotation:annotation]; } + + if (annotationView && [self.delegate respondsToSelector:@selector(mapView:didSelectAnnotationView:)]) + { + [self.delegate mapView:self didSelectAnnotationView:annotationView]; + } } - (MGLCompactCalloutView *)calloutViewForAnnotation:(id <MGLAnnotation>)annotation @@ -3546,6 +3608,17 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) { // dismiss popup [self.calloutViewForSelectedAnnotation dismissCalloutAnimated:animated]; + + // deselect annotation view + MGLAnnotationView *annotationView = nil; + MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation]; + + if (annotationTag != MGLAnnotationTagNotFound) + { + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + annotationView = annotationContext.annotationView; + annotationView.selected = NO; + } // clean up self.calloutViewForSelectedAnnotation = nil; @@ -3556,6 +3629,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) { [self.delegate mapView:self didDeselectAnnotation:annotation]; } + + if (annotationView && [self.delegate respondsToSelector:@selector(mapView:didDeselectAnnotationView:)]) + { + [self.delegate mapView:self didDeselectAnnotationView:annotationView]; + } } } @@ -4446,11 +4524,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) case mbgl::MapChangeDidFinishRenderingFrame: case mbgl::MapChangeDidFinishRenderingFrameFullyRendered: { + [self updateAnnotationViews]; if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) { [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:(change == mbgl::MapChangeDidFinishRenderingFrameFullyRendered)]; } - [self updateAnnotationViews]; break; } } @@ -4472,7 +4550,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) for (auto &pair : _annotationContextsByAnnotationTag) { - CGRect viewPort = CGRectInset(self.bounds, -_largestAnnotationViewSize.width - MGLAnnotationUpdateViewportOutset.width, -_largestAnnotationViewSize.height - MGLAnnotationUpdateViewportOutset.width); + CGRect viewPort = CGRectInset(self.bounds, + -_largestAnnotationViewSize.width / 2.0 - MGLAnnotationUpdateViewportOutset.width / 2.0, + -_largestAnnotationViewSize.height / 2.0 - MGLAnnotationUpdateViewportOutset.width); MGLAnnotationContext &annotationContext = pair.second; MGLAnnotationView *annotationView = annotationContext.annotationView; @@ -4490,7 +4570,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) CGPoint center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]; [annotationView setCenter:center pitch:self.camera.pitch]; - + annotationView.mapView = self; annotationContext.annotationView = annotationView; } } @@ -4514,6 +4594,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration) if (!annotationView) return; + annotationView.annotation = nil; + if (annotationContext.viewReuseIdentifier) { NSMutableArray *annotationViewReuseQueue = [self annotationViewReuseQueueForIdentifier:annotationContext.viewReuseIdentifier]; |