From 7401aabca6d2c99955e392061f46ba27ff5a3053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Sun, 17 Apr 2016 16:43:05 -0700 Subject: [PATCH] [ios] Observe annotation coordinates MGLMapView observes changes to the coordinate property of each MGLAnnotation added to it. Changing the coordinate property in a KVO-compliant way causes the annotation to be relocated and its callout view, if present, to be dismissed. To avoid observing the same annotation twice yet also avoid expensive lookups when adding or removing annotations, MGLMapView indexes added point annotations in an NSMutableSet. In iosapp, tapping a callout view moves the selected annotation to the center of the screen and deselects it. Fixes #1980. --- platform/ios/CHANGELOG.md | 1 + platform/ios/app/MBXViewController.m | 11 +++++++++++ platform/ios/src/MGLMapView.mm | 29 ++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 794947c90ec..7832d8cfb30 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. If you’d like - Applications linking against the SDK static framework no longer need to add `-ObjC` to the Other Linker Flags (`OTHER_LDFLAGS`) build setting. If you previously added this flag solely for this SDK, removing the flag may potentially reduce the overall size of your application. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641)) - Removed the `armv7s` slice from the SDK to reduce its size. iPhone 5 and iPhone 5c automatically use the `armv7` slice instead. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641)) - User location heading updates now resume properly when an app becomes active again. ([#4674](https://github.com/mapbox/mapbox-gl-native/pull/4674)) +- An MGLAnnotation can be relocated by changing its `coordinate` property in a KVO-compliant way. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835)) - Setting the `image` property of an MGLAnnotationImage to `nil` resets it to the default red pin image and reclaims resources that can be used to customize additional annotations. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835)) - A more specific user agent string is now sent with style and tile requests. ([#4012](https://github.com/mapbox/mapbox-gl-native/pull/4012)) - Removed unused SVG files from the SDK’s resource bundle. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641)) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 39093ccb386..d38b954c32a 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -695,4 +695,15 @@ - (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUse return nil; } +- (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id )annotation +{ + if ( ! [annotation isKindOfClass:[MGLPointAnnotation class]]) + { + return; + } + + MGLPointAnnotation *point = annotation; + point.coordinate = [self.mapView convertPoint:self.mapView.center toCoordinateFromView:self.mapView]; +} + @end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index e59b7923a47..a2cbabc2665 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -193,6 +193,7 @@ @implementation MGLMapView NS_MUTABLE_ARRAY_OF(NSURL *) *_bundledStyleURLs; MGLAnnotationContextMap _annotationContextsByAnnotationTag; + NS_MUTABLE_SET_OF(id ) *_pointAnnotations; /// Tag of the selected annotation. If the user location annotation is selected, this ivar is set to `MGLAnnotationTagNotFound`. MGLAnnotationTag _selectedAnnotationTag; BOOL _userLocationAnnotationIsSelected; @@ -337,6 +338,7 @@ - (void)commonInit // Set up annotation management and selection state. _annotationImagesByIdentifier = [NSMutableDictionary dictionary]; _annotationContextsByAnnotationTag = {}; + _pointAnnotations = [NSMutableSet set]; _selectedAnnotationTag = MGLAnnotationTagNotFound; _annotationsNearbyLastTap = {}; @@ -1609,6 +1611,21 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N [MGLMapboxEvents ensureMetricsOptoutExists]; } } + else if ([keyPath isEqualToString:@"coordinate"] && [object conformsToProtocol:@protocol(MGLAnnotation)]) + { + id annotation = object; + MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation]; + if (annotationTag != MGLAnnotationTagNotFound) + { + const mbgl::LatLng latLng = MGLLatLngFromLocationCoordinate2D(annotation.coordinate); + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + _mbglMap->updatePointAnnotation(annotationTag, { latLng, annotationImage.styleIconIdentifier.UTF8String ?: "" }); + if (annotationTag == _selectedAnnotationTag) + { + [self deselectAnnotation:annotation animated:YES]; + } + } + } } + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled @@ -2418,6 +2435,12 @@ - (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations [annotationImages addObject:annotationImage]; points.emplace_back(MGLLatLngFromLocationCoordinate2D(annotation.coordinate), symbolName.UTF8String ?: ""); + + if ( ! [_pointAnnotations containsObject:annotation] && [annotation isKindOfClass:[NSObject class]]) + { + [(NSObject *)annotation addObserver:self forKeyPath:@"coordinate" options:0 context:NULL]; + [_pointAnnotations addObject:annotation]; + } } } @@ -2572,6 +2595,12 @@ - (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations } _annotationContextsByAnnotationTag.erase(annotationTag); + + if ([annotation isKindOfClass:[NSObject class]]) + { + [(NSObject *)annotation removeObserver:self forKeyPath:@"coordinate"]; + } + [_pointAnnotations removeObject:annotation]; } if ( ! annotationTagsToRemove.empty())