Skip to content

Commit cb874a5

Browse files
christoph-jerolimovfacebook-github-bot-4
authored andcommitted
Add MapView annotation callback when it gets / lost the focus
Summary: For my project it was required to receive a notification when the MapView annotation was deselected. So I renamed `onAnnotationPress` to `onAnnotationSelected` and added a new method `onAnnotationDeselected`, this names was "inspired" by the underlaying iOS API. The old API was still called and marked as deprecated. But maybe you have an idea for a better naming (onAnnotationFocus/-Blur?) -- or should a deselected call the press method again without an annotation (undefined)? Closes facebook/react-native#5167 Reviewed By: svcscm Differential Revision: D2869695 Pulled By: nicklockwood fb-gh-sync-id: 91795ac3f1e4533b250af8901534d8870729d9db
1 parent 53100ec commit cb874a5

4 files changed

Lines changed: 100 additions & 34 deletions

File tree

Examples/UIExplorer/MapViewExample.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@ exports.examples = [
320320
}}/>;
321321
}
322322
},
323+
{
324+
title: 'Annotation focus example',
325+
render() {
326+
return <AnnotationExample style={styles.map} annotation={{
327+
title: 'More Info...',
328+
onFocus: () => {
329+
alert('Annotation gets focus');
330+
},
331+
onBlur: () => {
332+
alert('Annotation lost focus');
333+
}
334+
}}/>;
335+
}
336+
},
323337
{
324338
title: 'Draggable pin',
325339
render() {

Libraries/Components/MapView/MapView.js

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,18 @@ const MapView = React.createClass({
172172
*/
173173
onDragStateChange: React.PropTypes.func,
174174

175+
/**
176+
* Event that fires when the annotation gets was tapped by the user
177+
* and the callout view was displayed.
178+
*/
179+
onFocus: React.PropTypes.func,
180+
181+
/**
182+
* Event that fires when another annotation or the mapview itself
183+
* was tapped and a previously shown annotation will be closed.
184+
*/
185+
onBlur: React.PropTypes.func,
186+
175187
/**
176188
* Annotation title/subtile.
177189
*/
@@ -288,7 +300,7 @@ const MapView = React.createClass({
288300
onRegionChangeComplete: React.PropTypes.func,
289301

290302
/**
291-
* Callback that is called once, when the user taps an annotation.
303+
* Deprecated. Use annotation onFocus and onBlur instead.
292304
*/
293305
onAnnotationPress: React.PropTypes.func,
294306

@@ -376,51 +388,61 @@ const MapView = React.createClass({
376388
return result;
377389
});
378390

391+
const findByAnnotationId = (annotationId: string) => {
392+
if (!annotations) {
393+
return null;
394+
}
395+
for (let i = 0, l = annotations.length; i < l; i++) {
396+
if (annotations[i].id === annotationId) {
397+
return annotations[i];
398+
}
399+
}
400+
return null;
401+
};
402+
379403
// TODO: these should be separate events, to reduce bridge traffic
404+
let onPress, onAnnotationDragStateChange, onAnnotationFocus, onAnnotationBlur;
380405
if (annotations) {
381-
var onPress = (event: Event) => {
382-
if (!annotations) {
383-
return;
384-
}
406+
onPress = (event: Event) => {
385407
if (event.nativeEvent.action === 'annotation-click') {
408+
// TODO: Remove deprecated onAnnotationPress API call later.
386409
this.props.onAnnotationPress &&
387410
this.props.onAnnotationPress(event.nativeEvent.annotation);
388411
} else if (event.nativeEvent.action === 'callout-click') {
389-
// Find the annotation with the id that was pressed
390-
for (let i = 0, l = annotations.length; i < l; i++) {
391-
let annotation = annotations[i];
392-
if (annotation.id === event.nativeEvent.annotationId) {
393-
// Pass the right function
394-
if (event.nativeEvent.side === 'left') {
395-
annotation.onLeftCalloutPress &&
396-
annotation.onLeftCalloutPress(event.nativeEvent);
397-
} else if (event.nativeEvent.side === 'right') {
398-
annotation.onRightCalloutPress &&
399-
annotation.onRightCalloutPress(event.nativeEvent);
400-
}
401-
break;
412+
const annotation = findByAnnotationId(event.nativeEvent.annotationId);
413+
if (annotation) {
414+
// Pass the right function
415+
if (event.nativeEvent.side === 'left' && annotation.onLeftCalloutPress) {
416+
annotation.onLeftCalloutPress(event.nativeEvent);
417+
} else if (event.nativeEvent.side === 'right' && annotation.onRightCalloutPress) {
418+
annotation.onRightCalloutPress(event.nativeEvent);
402419
}
403420
}
404421
}
405422
};
406-
var onAnnotationDragStateChange = (event: Event) => {
407-
if (!annotations) {
408-
return;
423+
onAnnotationDragStateChange = (event: Event) => {
424+
const annotation = findByAnnotationId(event.nativeEvent.annotationId);
425+
if (annotation) {
426+
// Update location
427+
annotation.latitude = event.nativeEvent.latitude;
428+
annotation.longitude = event.nativeEvent.longitude;
429+
// Call callback
430+
annotation.onDragStateChange &&
431+
annotation.onDragStateChange(event.nativeEvent);
409432
}
410-
// Find the annotation with the id that was pressed
411-
for (let i = 0, l = annotations.length; i < l; i++) {
412-
let annotation = annotations[i];
413-
if (annotation.id === event.nativeEvent.annotationId) {
414-
// Update location
415-
annotation.latitude = event.nativeEvent.latitude;
416-
annotation.longitude = event.nativeEvent.longitude;
417-
// Call callback
418-
annotation.onDragStateChange &&
419-
annotation.onDragStateChange(event.nativeEvent);
420-
break;
421-
}
433+
};
434+
onAnnotationFocus = (event: Event) => {
435+
const annotation = findByAnnotationId(event.nativeEvent.annotationId);
436+
if (annotation && annotation.onFocus) {
437+
annotation.onFocus(event.nativeEvent);
422438
}
423-
}
439+
};
440+
onAnnotationBlur = (event: Event) => {
441+
const annotation = findByAnnotationId(event.nativeEvent.annotationId);
442+
if (annotation && annotation.onBlur) {
443+
annotation.onBlur(event.nativeEvent);
444+
}
445+
};
424446
}
425447

426448
// TODO: these should be separate events, to reduce bridge traffic
@@ -451,6 +473,8 @@ const MapView = React.createClass({
451473
onPress={onPress}
452474
onChange={onChange}
453475
onAnnotationDragStateChange={onAnnotationDragStateChange}
476+
onAnnotationFocus={onAnnotationFocus}
477+
onAnnotationBlur={onAnnotationBlur}
454478
/>
455479
);
456480
},
@@ -484,6 +508,8 @@ MapView.PinColors = PinColors && {
484508
const RCTMap = requireNativeComponent('RCTMap', MapView, {
485509
nativeOnly: {
486510
onAnnotationDragStateChange: true,
511+
onAnnotationFocus: true,
512+
onAnnotationBlur: true,
487513
onChange: true,
488514
onPress: true
489515
}

React/Views/RCTMap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ RCT_EXTERN const CGFloat RCTMapZoomBoundBuffer;
3131
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
3232
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
3333
@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationDragStateChange;
34+
@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationFocus;
35+
@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationBlur;
3436

3537
- (void)setAnnotations:(NSArray<RCTMapAnnotation *> *)annotations;
3638
- (void)setOverlays:(NSArray<RCTMapOverlay *> *)overlays;

React/Views/RCTMapManager.m

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ - (UIView *)view
9797
RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray<RCTMapAnnotation *>)
9898
RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray<RCTMapOverlay *>)
9999
RCT_EXPORT_VIEW_PROPERTY(onAnnotationDragStateChange, RCTBubblingEventBlock)
100+
RCT_EXPORT_VIEW_PROPERTY(onAnnotationFocus, RCTBubblingEventBlock)
101+
RCT_EXPORT_VIEW_PROPERTY(onAnnotationBlur, RCTBubblingEventBlock)
100102
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
101103
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
102104
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
@@ -137,6 +139,7 @@ - (UIView *)view
137139

138140
- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view
139141
{
142+
// TODO: Remove deprecated onAnnotationPress API call later.
140143
if (mapView.onPress && [view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
141144
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
142145
mapView.onPress(@{
@@ -150,6 +153,27 @@ - (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)vi
150153
}
151154
});
152155
}
156+
157+
if ([view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
158+
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
159+
if (mapView.onAnnotationFocus) {
160+
mapView.onAnnotationFocus(@{
161+
@"annotationId": annotation.identifier
162+
});
163+
}
164+
}
165+
}
166+
167+
- (void)mapView:(RCTMap *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
168+
{
169+
if ([view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
170+
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
171+
if (mapView.onAnnotationBlur) {
172+
mapView.onAnnotationBlur(@{
173+
@"annotationId": annotation.identifier
174+
});
175+
}
176+
}
153177
}
154178

155179
- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view

0 commit comments

Comments
 (0)