From 086297d59e4ac77fc873eb98b4fdbefffd8a3765 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 3 Oct 2016 16:17:21 -0700 Subject: [PATCH] [ios, macos] fixes #6160: allow updating multipoint coordinates --- platform/darwin/src/MGLMultiPoint.h | 10 +++++++ platform/darwin/src/MGLMultiPoint.mm | 43 +++++++++++++++++++++------- platform/ios/src/MGLMapView.mm | 29 +++++++++++++++++++ 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h index 59d9cd94291..20f5cbe56f0 100644 --- a/platform/darwin/src/MGLMultiPoint.h +++ b/platform/darwin/src/MGLMultiPoint.h @@ -35,6 +35,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range; +/** + Updates the coordinates for the shape, which will instantaneously redraw the + shape if it is currently visible on the map. + + @param coords The array of coordinates defining the shape. The data in this + array is copied to the object. + @param count The number of items in the `coords` array. + */ +- (void)updateCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm index a5b9eb8efc2..75e50530cc9 100644 --- a/platform/darwin/src/MGLMultiPoint.mm +++ b/platform/darwin/src/MGLMultiPoint.mm @@ -25,21 +25,28 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords if (self) { - _count = count; - _coordinates = (CLLocationCoordinate2D *)malloc(_count * sizeof(CLLocationCoordinate2D)); + [self setupWithCoordinates:coords count:count]; + } + + return self; +} + +- (void)setupWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count +{ + if (_coordinates) free(_coordinates); - mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); + _count = count; + _coordinates = (CLLocationCoordinate2D *)malloc(_count * sizeof(CLLocationCoordinate2D)); - for (NSUInteger i = 0; i < _count; i++) - { - _coordinates[i] = coords[i]; - bounds.extend(mbgl::LatLng(coords[i].latitude, coords[i].longitude)); - } + mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); - _bounds = MGLCoordinateBoundsFromLatLngBounds(bounds); + for (NSUInteger i = 0; i < _count; i++) + { + _coordinates[i] = coords[i]; + bounds.extend(mbgl::LatLng(coords[i].latitude, coords[i].longitude)); } - return self; + _bounds = MGLCoordinateBoundsFromLatLngBounds(bounds); } - (void)dealloc @@ -93,6 +100,22 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range } } +- (void)updateCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count +{ + if ([self isMemberOfClass:[MGLMultiPoint class]]) + { + [[NSException exceptionWithName:@"MGLAbstractClassException" + reason:@"MGLMultiPoint is an abstract class" + userInfo:nil] raise]; + } + + assert(_count > 0); + + [self willChangeValueForKey:@"coordinates"]; + [self setupWithCoordinates:coords count:count]; + [self didChangeValueForKey:@"coordinates"]; +} + - (MGLCoordinateBounds)overlayBounds { return _bounds; diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 4d660f8f351..f9477bb5c9b 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1795,6 +1795,29 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } } } + else if ([keyPath isEqualToString:@"coordinates"] && [object isKindOfClass:[MGLMultiPoint class]]) + { + MGLMultiPoint *annotation = object; + MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context; + // We can get here because a subclass registered itself as an observer + // of the coordinates key path of a multipoint annotation but failed + // to handle the change. This check deters us from treating the + // subclass’s context as an annotation tag. If the context happens to + // match a valid annotation tag, the annotation will be unnecessarily + // but safely updated. + if (annotation == [self annotationWithTag:annotationTag]) + { + // Update the annotation’s backing geometry to match the annotation model object. + _mbglMap->updateAnnotation(annotationTag, [annotation annotationObjectWithDelegate:self]); + + // We don't current support shape multipoint annotation selection, but let's make sure + // deselection is handled just to avoid problems in the future. + if (annotationTag == _selectedAnnotationTag) + { + [self deselectAnnotation:annotation animated:YES]; + } + } + } } + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled @@ -2840,6 +2863,8 @@ - (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations MGLAnnotationContext context; context.annotation = annotation; _annotationContextsByAnnotationTag[annotationTag] = context; + + [(NSObject *)annotation addObserver:self forKeyPath:@"coordinates" options:0 context:(void *)(NSUInteger)annotationTag]; } else if ( ! [annotation isKindOfClass:[MGLMultiPolyline class]] || ![annotation isKindOfClass:[MGLMultiPolygon class]] @@ -3132,6 +3157,10 @@ - (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations { [(NSObject *)annotation removeObserver:self forKeyPath:@"coordinate" context:(void *)(NSUInteger)annotationTag]; } + else if ([annotation isKindOfClass:[MGLMultiPoint class]]) + { + [(NSObject *)annotation removeObserver:self forKeyPath:@"coordinates" context:(void *)(NSUInteger)annotationTag]; + } _mbglMap->removeAnnotation(annotationTag); }