diff --git a/API.md b/API.md index a52fa082b..d6e226dd9 100644 --- a/API.md +++ b/API.md @@ -30,6 +30,7 @@ import { MapView } from 'react-native-mapbox-gl'; | `scrollEnabled` | `boolean` | Optional | Whether the map can be scrolled. | `true` | | `zoomEnabled` | `boolean` | Optional | Whether the map zoom level can be changed. | `true` | | `pitchEnabled` | `boolean` | Optional | Whether the map pitch (tilt) level can be changed via a two-finger drag (iOS) or three-finger drag (Android). | `true` | +| `annotationsPopUpEnabled` | `boolean` | Optional | Whether annotations popups can be shown. | `true` | | `showsUserLocation` | `boolean` | Optional | Whether the user's location is shown on the map. Note: The map will not zoom to their location. | `false` | | `userTrackingMode` | `enum` | Optional | Wether the map is zoomed to and follows the user's location. One of `Mapbox.userTrackingMode.none`, `Mapbox.userTrackingMode.follow`, `Mapbox.userTrackingMode.followWithCourse`, `Mapbox.userTrackingMode.followWithHeading` | `Mapbox.userTrackingMode.none` | | `userLocationVerticalAlignment` | `enum` | Optional | Change the alignment of where the user location shows on the screen. One of `Mapbox.userLocationVerticalAlignment.top`, `Mapbox.userLocationVerticalAlignment.center`, `Mapbox.userLocationVerticalAlignment.bottom` | `Mapbox.userLocationVerticalAlignment.center` | @@ -183,6 +184,14 @@ Selects the annotation tagged with `id`, as if it would be tapped by the user. The transition is animated unless you pass `animated` as `false`. +--- + +```javascript +this._map.deselectAnnotation(); +``` + +Deselects the previously selected annotation. + ## Styles #### Default styles diff --git a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLManager.java b/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLManager.java index adaba9304..7497818e0 100644 --- a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLManager.java +++ b/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLManager.java @@ -136,6 +136,11 @@ public void setPitchEnabled(ReactNativeMapboxGLView view, boolean value) { view.setPitchEnabled(value); } + @ReactProp(name = "annotationsPopUpEnabled") + public void setAnnotationsPopUpEnabled(ReactNativeMapboxGLView view, boolean value) { + view.setAnnotationsPopUpEnabled(value); + } + @ReactProp(name = "showsUserLocation") public void setShowsUserLocation(ReactNativeMapboxGLView view, boolean value) { view.setShowsUserLocation(value); @@ -182,6 +187,7 @@ public void setContentInset(ReactNativeMapboxGLView view, ReadableArray inset) { public static final int COMMAND_SET_VISIBLE_COORDINATE_BOUNDS = 6; public static final int COMMAND_SELECT_ANNOTATION = 7; public static final int COMMAND_SPLICE_ANNOTATIONS = 8; + public static final int COMMAND_DESELECT_ANNOTATION = 9; @Override public @@ -196,6 +202,7 @@ Map getCommandsMap() { .put("setVisibleCoordinateBounds", COMMAND_SET_VISIBLE_COORDINATE_BOUNDS) .put("selectAnnotation", COMMAND_SELECT_ANNOTATION) .put("spliceAnnotations", COMMAND_SPLICE_ANNOTATIONS) + .put("deselectAnnotation", COMMAND_DESELECT_ANNOTATION) .build(); } @@ -240,6 +247,9 @@ public void receiveCommand(ReactNativeMapboxGLView view, int commandId, @Nullabl case COMMAND_SPLICE_ANNOTATIONS: spliceAnnotations(view, args.getBoolean(0), args.getArray(1), args.getArray(2)); break; + case COMMAND_DESELECT_ANNOTATION: + deselectAnnotation(view); + break; default: throw new JSApplicationIllegalArgumentException("Invalid commandId " + commandId + " sent to " + getClass().getSimpleName()); } @@ -376,4 +386,8 @@ public void spliceAnnotations(ReactNativeMapboxGLView view, boolean removeAll, R public void selectAnnotation(ReactNativeMapboxGLView view, String annotationId, boolean animated) { view.selectAnnotation(annotationId, animated); } + + public void deselectAnnotation(ReactNativeMapboxGLView view) { + view.deselectAnnotation(); + } } diff --git a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLView.java b/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLView.java index f0164e0c3..eb2d7021e 100644 --- a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLView.java +++ b/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLView.java @@ -55,6 +55,7 @@ public class ReactNativeMapboxGLView extends RelativeLayout implements private int _bearingTrackingMode; private boolean _trackingModeUpdateScheduled = false; private boolean _showsUserLocation; + private boolean _annotationsPopUpEnabled = true; private boolean _zoomEnabled = true; private boolean _pitchEnabled = true; private boolean _scrollEnabled = true; @@ -62,7 +63,6 @@ public class ReactNativeMapboxGLView extends RelativeLayout implements private boolean _enableOnRegionWillChange = false; private boolean _enableOnRegionDidChange = false; private int _paddingTop, _paddingRight, _paddingBottom, _paddingLeft; - private boolean _recentlyChanged = false; private boolean _willChangeThrottled = false; private boolean _didChangeThrottled = false; @@ -262,6 +262,10 @@ public void setPitchEnabled(boolean value) { } } + public void setAnnotationsPopUpEnabled(boolean value) { + _annotationsPopUpEnabled = value; + } + public void setStyleURL(String styleURL) { if (styleURL.equals(_mapOptions.getStyle())) { return; } _mapOptions.styleUrl(styleURL); @@ -271,19 +275,19 @@ public void setStyleURL(String styleURL) { public void setDebugActive(boolean value) { if (_mapOptions.getDebugActive() == value) { return; } _mapOptions.debugActive(value); - if (_map != null) { _map.setDebugActive(value); }; + if (_map != null) { _map.setDebugActive(value); } } public void setLocationTracking(int value) { if (_locationTrackingMode == value) { return; } _locationTrackingMode = value; - if (_map != null) { _map.getTrackingSettings().setMyLocationTrackingMode(value); }; + if (_map != null) { _map.getTrackingSettings().setMyLocationTrackingMode(value); } } public void setBearingTracking(int value) { if (_bearingTrackingMode == value) { return; } _bearingTrackingMode = value; - if (_map != null) { _map.getTrackingSettings().setMyBearingTrackingMode(value); }; + if (_map != null) { _map.getTrackingSettings().setMyBearingTrackingMode(value); } } public void setAttributionButtonIsHidden(boolean value) { @@ -556,6 +560,7 @@ public boolean onInfoWindowClick(@NonNull Marker marker) { public boolean onMarkerClick(@NonNull Marker marker) { emitEvent("onOpenAnnotation", serializeMarker(marker)); + if (_annotationsPopUpEnabled == false) { return true; } // Due to a bug, we need to force a relayout on the _mapView _handler.post(new Runnable() { @Override @@ -578,7 +583,7 @@ public CameraPosition getCameraPosition() { } public LatLngBounds getBounds() { - if (_map == null) { return new LatLngBounds.Builder().build(); }; + if (_map == null) { return new LatLngBounds.Builder().build(); } return _map.getProjection().getVisibleRegion().latLngBounds; } @@ -614,7 +619,6 @@ class CameraCallback implements MapboxMap.CancelableCallback { public void onCancel() { if (callback != null) { callback.run(); } } - @Override public void onFinish() { if (callback != null) { callback.run(); } @@ -676,4 +680,9 @@ public void selectAnnotation(String name, boolean animated) { Marker marker = (Marker)annotation; _map.selectMarker(marker); } + + public void deselectAnnotation() { + if (_map == null) { return; } + _map.deselectMarkers(); + } } diff --git a/example.js b/example.js index 262825965..57bb41a80 100644 --- a/example.js +++ b/example.js @@ -219,6 +219,9 @@ class MapExample extends Component { this._map && this._map.selectAnnotation('marker1')}> Open marker1 popup + this._map && this._map.deselectAnnotation()}> + Deselect annotation + Remove marker2 annotation diff --git a/index.js b/index.js index 7169dd29c..0c9a445bd 100644 --- a/index.js +++ b/index.js @@ -228,7 +228,9 @@ class MapView extends Component { selectAnnotation(annotationId, animated = true) { MapboxGLManager.selectAnnotation(findNodeHandle(this), annotationId, animated); } - + deselectAnnotation() { + MapboxGLManager.deselectAnnotation(findNodeHandle(this)); + } // Events _onRegionDidChange(event: Event) { if (this.props.onRegionDidChange) this.props.onRegionDidChange(event.nativeEvent.src); @@ -279,6 +281,7 @@ class MapView extends Component { scrollEnabled: PropTypes.bool, zoomEnabled: PropTypes.bool, pitchEnabled: PropTypes.bool, + annotationsPopUpEnabled: PropTypes.bool, showsUserLocation: PropTypes.bool, styleURL: PropTypes.string.isRequired, userTrackingMode: PropTypes.number, @@ -340,6 +343,7 @@ class MapView extends Component { styleURL: mapStyles.streets, userTrackingMode: userTrackingMode.none, zoomEnabled: true, + annotationsPopUpEnabled: true, attributionButtonIsHidden: false, logoIsHidden: false, compassIsHidden: false, diff --git a/ios/RCTMapboxGL/RCTMapboxGL.h b/ios/RCTMapboxGL/RCTMapboxGL.h index 6b65d3582..0f2930f70 100644 --- a/ios/RCTMapboxGL/RCTMapboxGL.h +++ b/ios/RCTMapboxGL/RCTMapboxGL.h @@ -32,12 +32,14 @@ - (void)setCompassIsHidden:(BOOL)isHidden; - (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment) aligment; - (void)setContentInset:(UIEdgeInsets)contentInset; +@property (nonatomic) BOOL annotationsPopUpEnabled; // Imperative methods - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinates zoomLevel:(double)zoomLevel direction:(double)direction animated:(BOOL)animated completionHandler:(void (^)())callback; - (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))handler; - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)padding animated:(BOOL)animated; - (void)selectAnnotation:(NSString*)selectedId animated:(BOOL)animated; +- (void)deselectAnnotation; // Annotation management - (void)upsertAnnotation:(NSObject *)annotation; diff --git a/ios/RCTMapboxGL/RCTMapboxGL.m b/ios/RCTMapboxGL/RCTMapboxGL.m index a0e6e0991..569097a7f 100644 --- a/ios/RCTMapboxGL/RCTMapboxGL.m +++ b/ios/RCTMapboxGL/RCTMapboxGL.m @@ -40,7 +40,6 @@ @implementation RCTMapboxGL { BOOL _compass; UIEdgeInsets _contentInset; MGLAnnotationVerticalAlignment _userLocationVerticalAlignment; - /* So we don't fire onChangeUserTracking mode when triggered by props */ BOOL _isChangingUserTracking; } @@ -68,12 +67,12 @@ - (void)createMapIfNeeded ) { return; } - + if (![MGLAccountManager accessToken]) { RCTLogError(@"You need an access token to use Mapbox. Register to mapbox.com to obtain one, then run Mapbox.setAccessToken(yourToken) before mounting this component"); return; } - + _map = [[MGLMapView alloc] initWithFrame:self.bounds]; _map.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _map.delegate = self; @@ -107,7 +106,7 @@ - (void)createMapIfNeeded for (NSString * annotationId in _annotations) { [_map addAnnotation:_annotations[annotationId]]; } - + [self addSubview:_map]; [self layoutSubviews]; } @@ -133,7 +132,7 @@ - (void)upsertAnnotation:(RCTMGLAnnotation *) annotation { RCTLogError(@"field `id` is required on all annotations"); return; } - + RCTMGLAnnotation * oldAnnotation = [_annotations objectForKey:identifier]; [_annotations setObject:annotation forKey:identifier]; [_map addAnnotation:annotation]; @@ -156,6 +155,15 @@ - (void)removeAllAnnotations [_annotations removeAllObjects]; } +- (void)deselectAnnotation +{ + NSArray * annotations = [_map selectedAnnotations]; + if (!annotations) { return; } + for (id annotation in annotations) { + [_map deselectAnnotation:annotation animated:YES]; + } +} + - (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(RCTMGLAnnotationPolyline *)shape { if ([shape isKindOfClass:[RCTMGLAnnotationPolyline class]]) { @@ -189,6 +197,7 @@ - (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(RCTMGL } - (BOOL)mapView:(RCTMapboxGL *)mapView annotationCanShowCallout:(id )annotation { + if (!_annotationsPopUpEnabled) { return NO; } NSString *title = [(RCTMGLAnnotation *) annotation title]; NSString *subtitle = [(RCTMGLAnnotation *) annotation subtitle]; return ([title length] != 0 || [subtitle length] != 0); @@ -206,16 +215,16 @@ - (UIButton *)mapView:(MGLMapView *)mapView rightCalloutAccessoryViewForAnnotati - (void)mapView:(MGLMapView *)mapView annotation:(id)annotation calloutAccessoryControlTapped:(UIControl *)control { if (annotation.title && annotation.subtitle) { - + NSString *id = [(RCTMGLAnnotation *) annotation id]; - + NSDictionary *event = @{ @"target": self.reactTag, @"src": @{ @"title": annotation.title, @"subtitle": annotation.subtitle, @"id": id, @"latitude": @(annotation.coordinate.latitude), @"longitude": @(annotation.coordinate.longitude)} }; - + [_eventDispatcher sendInputEventWithName:@"onRightAnnotationTapped" body:event]; } } @@ -224,11 +233,11 @@ - (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id*)packs }]; } + +RCT_EXPORT_METHOD(deselectAnnotation:(nonnull NSNumber *) reactTag) +{ + [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RCTMapboxGL *mapView = viewRegistry[reactTag]; + if ([mapView isKindOfClass:[RCTMapboxGL class]]) { + [mapView deselectAnnotation]; + } + }]; +} @end