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 https://github.com/facebook/react-native/pull/5167

Reviewed By: svcscm

Differential Revision: D2869695

Pulled By: nicklockwood

fb-gh-sync-id: 91795ac3f1e4533b250af8901534d8870729d9db
This commit is contained in:
Christoph Jerolimov
2016-01-29 06:25:35 -08:00
committed by facebook-github-bot-4
parent 53100ecccb
commit cb874a55aa
4 changed files with 101 additions and 35 deletions

View File

@@ -320,6 +320,20 @@ exports.examples = [
}}/>; }}/>;
} }
}, },
{
title: 'Annotation focus example',
render() {
return <AnnotationExample style={styles.map} annotation={{
title: 'More Info...',
onFocus: () => {
alert('Annotation gets focus');
},
onBlur: () => {
alert('Annotation lost focus');
}
}}/>;
}
},
{ {
title: 'Draggable pin', title: 'Draggable pin',
render() { render() {

View File

@@ -172,6 +172,18 @@ const MapView = React.createClass({
*/ */
onDragStateChange: React.PropTypes.func, onDragStateChange: React.PropTypes.func,
/**
* Event that fires when the annotation gets was tapped by the user
* and the callout view was displayed.
*/
onFocus: React.PropTypes.func,
/**
* Event that fires when another annotation or the mapview itself
* was tapped and a previously shown annotation will be closed.
*/
onBlur: React.PropTypes.func,
/** /**
* Annotation title/subtile. * Annotation title/subtile.
*/ */
@@ -288,7 +300,7 @@ const MapView = React.createClass({
onRegionChangeComplete: React.PropTypes.func, onRegionChangeComplete: React.PropTypes.func,
/** /**
* Callback that is called once, when the user taps an annotation. * Deprecated. Use annotation onFocus and onBlur instead.
*/ */
onAnnotationPress: React.PropTypes.func, onAnnotationPress: React.PropTypes.func,
@@ -376,51 +388,61 @@ const MapView = React.createClass({
return result; return result;
}); });
// TODO: these should be separate events, to reduce bridge traffic const findByAnnotationId = (annotationId: string) => {
if (annotations) { if (!annotations) {
var onPress = (event: Event) => { return null;
if (!annotations) { }
return; for (let i = 0, l = annotations.length; i < l; i++) {
if (annotations[i].id === annotationId) {
return annotations[i];
} }
}
return null;
};
// TODO: these should be separate events, to reduce bridge traffic
let onPress, onAnnotationDragStateChange, onAnnotationFocus, onAnnotationBlur;
if (annotations) {
onPress = (event: Event) => {
if (event.nativeEvent.action === 'annotation-click') { if (event.nativeEvent.action === 'annotation-click') {
// TODO: Remove deprecated onAnnotationPress API call later.
this.props.onAnnotationPress && this.props.onAnnotationPress &&
this.props.onAnnotationPress(event.nativeEvent.annotation); this.props.onAnnotationPress(event.nativeEvent.annotation);
} else if (event.nativeEvent.action === 'callout-click') { } else if (event.nativeEvent.action === 'callout-click') {
// Find the annotation with the id that was pressed const annotation = findByAnnotationId(event.nativeEvent.annotationId);
for (let i = 0, l = annotations.length; i < l; i++) { if (annotation) {
let annotation = annotations[i]; // Pass the right function
if (annotation.id === event.nativeEvent.annotationId) { if (event.nativeEvent.side === 'left' && annotation.onLeftCalloutPress) {
// Pass the right function annotation.onLeftCalloutPress(event.nativeEvent);
if (event.nativeEvent.side === 'left') { } else if (event.nativeEvent.side === 'right' && annotation.onRightCalloutPress) {
annotation.onLeftCalloutPress && annotation.onRightCalloutPress(event.nativeEvent);
annotation.onLeftCalloutPress(event.nativeEvent);
} else if (event.nativeEvent.side === 'right') {
annotation.onRightCalloutPress &&
annotation.onRightCalloutPress(event.nativeEvent);
}
break;
} }
} }
} }
}; };
var onAnnotationDragStateChange = (event: Event) => { onAnnotationDragStateChange = (event: Event) => {
if (!annotations) { const annotation = findByAnnotationId(event.nativeEvent.annotationId);
return; if (annotation) {
// Update location
annotation.latitude = event.nativeEvent.latitude;
annotation.longitude = event.nativeEvent.longitude;
// Call callback
annotation.onDragStateChange &&
annotation.onDragStateChange(event.nativeEvent);
} }
// Find the annotation with the id that was pressed };
for (let i = 0, l = annotations.length; i < l; i++) { onAnnotationFocus = (event: Event) => {
let annotation = annotations[i]; const annotation = findByAnnotationId(event.nativeEvent.annotationId);
if (annotation.id === event.nativeEvent.annotationId) { if (annotation && annotation.onFocus) {
// Update location annotation.onFocus(event.nativeEvent);
annotation.latitude = event.nativeEvent.latitude;
annotation.longitude = event.nativeEvent.longitude;
// Call callback
annotation.onDragStateChange &&
annotation.onDragStateChange(event.nativeEvent);
break;
}
} }
} };
onAnnotationBlur = (event: Event) => {
const annotation = findByAnnotationId(event.nativeEvent.annotationId);
if (annotation && annotation.onBlur) {
annotation.onBlur(event.nativeEvent);
}
};
} }
// TODO: these should be separate events, to reduce bridge traffic // TODO: these should be separate events, to reduce bridge traffic
@@ -451,6 +473,8 @@ const MapView = React.createClass({
onPress={onPress} onPress={onPress}
onChange={onChange} onChange={onChange}
onAnnotationDragStateChange={onAnnotationDragStateChange} onAnnotationDragStateChange={onAnnotationDragStateChange}
onAnnotationFocus={onAnnotationFocus}
onAnnotationBlur={onAnnotationBlur}
/> />
); );
}, },
@@ -484,6 +508,8 @@ MapView.PinColors = PinColors && {
const RCTMap = requireNativeComponent('RCTMap', MapView, { const RCTMap = requireNativeComponent('RCTMap', MapView, {
nativeOnly: { nativeOnly: {
onAnnotationDragStateChange: true, onAnnotationDragStateChange: true,
onAnnotationFocus: true,
onAnnotationBlur: true,
onChange: true, onChange: true,
onPress: true onPress: true
} }

View File

@@ -31,6 +31,8 @@ RCT_EXTERN const CGFloat RCTMapZoomBoundBuffer;
@property (nonatomic, copy) RCTBubblingEventBlock onChange; @property (nonatomic, copy) RCTBubblingEventBlock onChange;
@property (nonatomic, copy) RCTBubblingEventBlock onPress; @property (nonatomic, copy) RCTBubblingEventBlock onPress;
@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationDragStateChange; @property (nonatomic, copy) RCTBubblingEventBlock onAnnotationDragStateChange;
@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationFocus;
@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationBlur;
- (void)setAnnotations:(NSArray<RCTMapAnnotation *> *)annotations; - (void)setAnnotations:(NSArray<RCTMapAnnotation *> *)annotations;
- (void)setOverlays:(NSArray<RCTMapOverlay *> *)overlays; - (void)setOverlays:(NSArray<RCTMapOverlay *> *)overlays;

View File

@@ -97,6 +97,8 @@ RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType)
RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray<RCTMapAnnotation *>) RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray<RCTMapAnnotation *>)
RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray<RCTMapOverlay *>) RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray<RCTMapOverlay *>)
RCT_EXPORT_VIEW_PROPERTY(onAnnotationDragStateChange, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onAnnotationDragStateChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onAnnotationFocus, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onAnnotationBlur, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
@@ -137,6 +139,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view - (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{ {
// TODO: Remove deprecated onAnnotationPress API call later.
if (mapView.onPress && [view.annotation isKindOfClass:[RCTMapAnnotation class]]) { if (mapView.onPress && [view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation; RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
mapView.onPress(@{ mapView.onPress(@{
@@ -150,6 +153,27 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
} }
}); });
} }
if ([view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
if (mapView.onAnnotationFocus) {
mapView.onAnnotationFocus(@{
@"annotationId": annotation.identifier
});
}
}
}
- (void)mapView:(RCTMap *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
if ([view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
if (mapView.onAnnotationBlur) {
mapView.onAnnotationBlur(@{
@"annotationId": annotation.identifier
});
}
}
} }
- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view - (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view