diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/MapViewExample.js index b10620b04a6043..d11d3006ac470b 100644 --- a/Examples/UIExplorer/MapViewExample.js +++ b/Examples/UIExplorer/MapViewExample.js @@ -320,6 +320,20 @@ exports.examples = [ }}/>; } }, + { + title: 'Annotation focus example', + render() { + return { + alert('Annotation gets focus'); + }, + onBlur: () => { + alert('Annotation lost focus'); + } + }}/>; + } + }, { title: 'Draggable pin', render() { diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index 5dbbf2d9f83cdd..13ee06ee8618d8 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -172,6 +172,18 @@ const MapView = React.createClass({ */ 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. */ @@ -288,7 +300,7 @@ const MapView = React.createClass({ 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, @@ -376,51 +388,61 @@ const MapView = React.createClass({ return result; }); + const findByAnnotationId = (annotationId: string) => { + if (!annotations) { + return null; + } + 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) { - var onPress = (event: Event) => { - if (!annotations) { - return; - } + onPress = (event: Event) => { if (event.nativeEvent.action === 'annotation-click') { + // TODO: Remove deprecated onAnnotationPress API call later. this.props.onAnnotationPress && this.props.onAnnotationPress(event.nativeEvent.annotation); } else if (event.nativeEvent.action === 'callout-click') { - // Find the annotation with the id that was pressed - for (let i = 0, l = annotations.length; i < l; i++) { - let annotation = annotations[i]; - if (annotation.id === event.nativeEvent.annotationId) { - // Pass the right function - if (event.nativeEvent.side === 'left') { - annotation.onLeftCalloutPress && - annotation.onLeftCalloutPress(event.nativeEvent); - } else if (event.nativeEvent.side === 'right') { - annotation.onRightCalloutPress && - annotation.onRightCalloutPress(event.nativeEvent); - } - break; + const annotation = findByAnnotationId(event.nativeEvent.annotationId); + if (annotation) { + // Pass the right function + if (event.nativeEvent.side === 'left' && annotation.onLeftCalloutPress) { + annotation.onLeftCalloutPress(event.nativeEvent); + } else if (event.nativeEvent.side === 'right' && annotation.onRightCalloutPress) { + annotation.onRightCalloutPress(event.nativeEvent); } } } }; - var onAnnotationDragStateChange = (event: Event) => { - if (!annotations) { - return; + onAnnotationDragStateChange = (event: Event) => { + const annotation = findByAnnotationId(event.nativeEvent.annotationId); + 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++) { - let annotation = annotations[i]; - if (annotation.id === event.nativeEvent.annotationId) { - // Update location - annotation.latitude = event.nativeEvent.latitude; - annotation.longitude = event.nativeEvent.longitude; - // Call callback - annotation.onDragStateChange && - annotation.onDragStateChange(event.nativeEvent); - break; - } + }; + onAnnotationFocus = (event: Event) => { + const annotation = findByAnnotationId(event.nativeEvent.annotationId); + if (annotation && annotation.onFocus) { + annotation.onFocus(event.nativeEvent); } - } + }; + 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 @@ -451,6 +473,8 @@ const MapView = React.createClass({ onPress={onPress} onChange={onChange} onAnnotationDragStateChange={onAnnotationDragStateChange} + onAnnotationFocus={onAnnotationFocus} + onAnnotationBlur={onAnnotationBlur} /> ); }, @@ -484,6 +508,8 @@ MapView.PinColors = PinColors && { const RCTMap = requireNativeComponent('RCTMap', MapView, { nativeOnly: { onAnnotationDragStateChange: true, + onAnnotationFocus: true, + onAnnotationBlur: true, onChange: true, onPress: true } diff --git a/React/Views/RCTMap.h b/React/Views/RCTMap.h index e7a0768107a15e..e6c5cabf0b80ba 100644 --- a/React/Views/RCTMap.h +++ b/React/Views/RCTMap.h @@ -31,6 +31,8 @@ RCT_EXTERN const CGFloat RCTMapZoomBoundBuffer; @property (nonatomic, copy) RCTBubblingEventBlock onChange; @property (nonatomic, copy) RCTBubblingEventBlock onPress; @property (nonatomic, copy) RCTBubblingEventBlock onAnnotationDragStateChange; +@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationFocus; +@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationBlur; - (void)setAnnotations:(NSArray *)annotations; - (void)setOverlays:(NSArray *)overlays; diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m index 74517b4c40dbf8..ec4595a5a3a584 100644 --- a/React/Views/RCTMapManager.m +++ b/React/Views/RCTMapManager.m @@ -97,6 +97,8 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray) RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray) 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(onPress, RCTBubblingEventBlock) RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) @@ -137,6 +139,7 @@ - (UIView *)view - (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view { + // TODO: Remove deprecated onAnnotationPress API call later. if (mapView.onPress && [view.annotation isKindOfClass:[RCTMapAnnotation class]]) { RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation; mapView.onPress(@{ @@ -150,6 +153,27 @@ - (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)vi } }); } + + 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