diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/ImageExample.js
index 163396f89..60fc38dab 100644
--- a/Examples/UIExplorer/ImageExample.js
+++ b/Examples/UIExplorer/ImageExample.js
@@ -442,7 +442,8 @@ exports.examples = [
title: 'Image Size',
render: function() {
return ;
- }
+ },
+ platform: 'ios',
},
];
diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/MapViewExample.js
index 3df5d4ded..498a5f83d 100644
--- a/Examples/UIExplorer/MapViewExample.js
+++ b/Examples/UIExplorer/MapViewExample.js
@@ -19,6 +19,7 @@ var React = require('react-native');
var {
Image,
MapView,
+ PropTypes,
StyleSheet,
Text,
TextInput,
@@ -33,36 +34,23 @@ var regionText = {
longitudeDelta: '0',
};
-type MapRegion = {
- latitude: number,
- longitude: number,
- latitudeDelta?: number,
- longitudeDelta?: number,
-};
-
-type MapRegionInputState = {
- region: MapRegion,
-};
-
var MapRegionInput = React.createClass({
propTypes: {
- region: React.PropTypes.shape({
- latitude: React.PropTypes.number.isRequired,
- longitude: React.PropTypes.number.isRequired,
- latitudeDelta: React.PropTypes.number,
- longitudeDelta: React.PropTypes.number,
+ region: PropTypes.shape({
+ latitude: PropTypes.number.isRequired,
+ longitude: PropTypes.number.isRequired,
+ latitudeDelta: PropTypes.number,
+ longitudeDelta: PropTypes.number,
}),
- onChange: React.PropTypes.func.isRequired,
+ onChange: PropTypes.func.isRequired,
},
- getInitialState(): MapRegionInputState {
+ getInitialState() {
return {
region: {
latitude: 0,
longitude: 0,
- latitudeDelta: 0,
- longitudeDelta: 0,
}
};
},
@@ -164,36 +152,14 @@ var MapRegionInput = React.createClass({
});
-type Annotations = Array<{
- animateDrop?: boolean,
- latitude: number,
- longitude: number,
- title?: string,
- subtitle?: string,
- hasLeftCallout?: boolean,
- hasRightCallout?: boolean,
- onLeftCalloutPress?: Function,
- onRightCalloutPress?: Function,
- tintColor?: number | string,
- image?: any,
- id?: string,
- view?: ReactElement,
- leftCalloutView?: ReactElement,
- rightCalloutView?: ReactElement,
- detailCalloutView?: ReactElement,
-}>;
-type MapViewExampleState = {
- isFirstLoad: boolean,
- mapRegion?: MapRegion,
- mapRegionInput?: MapRegion,
- annotations?: Annotations,
-};
-
var MapViewExample = React.createClass({
- getInitialState(): MapViewExampleState {
+ getInitialState() {
return {
isFirstLoad: true,
+ mapRegion: undefined,
+ mapRegionInput: undefined,
+ annotations: [],
};
},
@@ -215,7 +181,7 @@ var MapViewExample = React.createClass({
);
},
- _getAnnotations(region): Annotations {
+ _getAnnotations(region) {
return [{
longitude: region.longitude,
latitude: region.latitude,
@@ -249,16 +215,13 @@ var MapViewExample = React.createClass({
});
-type AnnotationExampleState = {
- isFirstLoad: boolean,
- annotations?: Annotations,
- mapRegion?: MapRegion,
-};
var AnnotationExample = React.createClass({
- getInitialState(): AnnotationExampleState {
+ getInitialState() {
return {
isFirstLoad: true,
+ annotations: [],
+ mapRegion: undefined,
};
},
@@ -351,6 +314,17 @@ exports.examples = [
}}/>;
}
},
+ {
+ title: 'Draggable pin',
+ render() {
+ return {
+ console.log('Drag state: ' + event.state);
+ },
+ }}/>;
+ }
+ },
{
title: 'Custom pin color',
render() {
diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js
index 29257d1db..2ccb4b8cb 100644
--- a/Libraries/Components/MapView/MapView.js
+++ b/Libraries/Components/MapView/MapView.js
@@ -28,7 +28,16 @@ const requireNativeComponent = require('requireNativeComponent');
type Event = Object;
+export type AnnotationDragState = $Enum<{
+ idle: string;
+ starting: string;
+ dragging: string;
+ canceling: string;
+ ending: string;
+}>;
+
const MapView = React.createClass({
+
mixins: [NativeMethodsMixin],
propTypes: {
@@ -144,6 +153,18 @@ const MapView = React.createClass({
* Whether the pin drop should be animated or not
*/
animateDrop: React.PropTypes.bool,
+
+ /**
+ * Whether the pin should be draggable or not
+ * @platform ios
+ */
+ draggable: React.PropTypes.bool,
+
+ /**
+ * Event that fires when the annotation drag state changes.
+ * @platform ios
+ */
+ onDragStateChange: React.PropTypes.func,
/**
* Annotation title/subtile.
@@ -376,6 +397,24 @@ const MapView = React.createClass({
}
}
};
+ var onAnnotationDragStateChange = (event: Event) => {
+ if (!annotations) {
+ return;
+ }
+ // 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;
+ }
+ }
+ }
}
// TODO: these should be separate events, to reduce bridge traffic
@@ -399,6 +438,7 @@ const MapView = React.createClass({
overlays={overlays}
onPress={onPress}
onChange={onChange}
+ onAnnotationDragStateChange={onAnnotationDragStateChange}
/>
);
},
@@ -430,7 +470,11 @@ MapView.PinColors = PinColors && {
};
const RCTMap = requireNativeComponent('RCTMap', MapView, {
- nativeOnly: {onChange: true, onPress: true}
+ nativeOnly: {
+ onAnnotationDragStateChange: true,
+ onChange: true,
+ onPress: true
+ }
});
module.exports = MapView;
diff --git a/React/Views/RCTConvert+MapKit.m b/React/Views/RCTConvert+MapKit.m
index 369ec2446..354b133b6 100644
--- a/React/Views/RCTConvert+MapKit.m
+++ b/React/Views/RCTConvert+MapKit.m
@@ -42,22 +42,23 @@ RCT_ENUM_CONVERTER(MKMapType, (@{
json = [self NSDictionary:json];
RCTMapAnnotation *annotation = [RCTMapAnnotation new];
annotation.coordinate = [self CLLocationCoordinate2D:json];
- annotation.title = [RCTConvert NSString:json[@"title"]];
- annotation.subtitle = [RCTConvert NSString:json[@"subtitle"]];
- annotation.identifier = [RCTConvert NSString:json[@"id"]];
- annotation.hasLeftCallout = [RCTConvert BOOL:json[@"hasLeftCallout"]];
- annotation.hasRightCallout = [RCTConvert BOOL:json[@"hasRightCallout"]];
- annotation.animateDrop = [RCTConvert BOOL:json[@"animateDrop"]];
- annotation.tintColor = [RCTConvert UIColor:json[@"tintColor"]];
- annotation.image = [RCTConvert UIImage:json[@"image"]];
+ annotation.draggable = [self BOOL:json[@"draggable"]];
+ annotation.title = [self NSString:json[@"title"]];
+ annotation.subtitle = [self NSString:json[@"subtitle"]];
+ annotation.identifier = [self NSString:json[@"id"]];
+ annotation.hasLeftCallout = [self BOOL:json[@"hasLeftCallout"]];
+ annotation.hasRightCallout = [self BOOL:json[@"hasRightCallout"]];
+ annotation.animateDrop = [self BOOL:json[@"animateDrop"]];
+ annotation.tintColor = [self UIColor:json[@"tintColor"]];
+ annotation.image = [self UIImage:json[@"image"]];
annotation.viewIndex =
- [RCTConvert NSInteger:json[@"viewIndex"] ?: @(NSNotFound)];
+ [self NSInteger:json[@"viewIndex"] ?: @(NSNotFound)];
annotation.leftCalloutViewIndex =
- [RCTConvert NSInteger:json[@"leftCalloutViewIndex"] ?: @(NSNotFound)];
+ [self NSInteger:json[@"leftCalloutViewIndex"] ?: @(NSNotFound)];
annotation.rightCalloutViewIndex =
- [RCTConvert NSInteger:json[@"rightCalloutViewIndex"] ?: @(NSNotFound)];
+ [self NSInteger:json[@"rightCalloutViewIndex"] ?: @(NSNotFound)];
annotation.detailCalloutViewIndex =
- [RCTConvert NSInteger:json[@"detailCalloutViewIndex"] ?: @(NSNotFound)];
+ [self NSInteger:json[@"detailCalloutViewIndex"] ?: @(NSNotFound)];
return annotation;
}
@@ -66,19 +67,19 @@ RCT_ARRAY_CONVERTER(RCTMapAnnotation)
+ (RCTMapOverlay *)RCTMapOverlay:(id)json
{
json = [self NSDictionary:json];
- NSArray *locations = [RCTConvert NSDictionaryArray:json[@"coordinates"]];
+ NSArray *locations = [self NSDictionaryArray:json[@"coordinates"]];
CLLocationCoordinate2D coordinates[locations.count];
NSUInteger index = 0;
for (NSDictionary *location in locations) {
- coordinates[index++] = [RCTConvert CLLocationCoordinate2D:location];
+ coordinates[index++] = [self CLLocationCoordinate2D:location];
}
RCTMapOverlay *overlay = [RCTMapOverlay polylineWithCoordinates:coordinates
count:locations.count];
- overlay.strokeColor = [RCTConvert UIColor:json[@"strokeColor"]];
- overlay.identifier = [RCTConvert NSString:json[@"id"]];
- overlay.lineWidth = [RCTConvert CGFloat:json[@"lineWidth"] ?: @1];
+ overlay.strokeColor = [self UIColor:json[@"strokeColor"]];
+ overlay.identifier = [self NSString:json[@"id"]];
+ overlay.lineWidth = [self CGFloat:json[@"lineWidth"] ?: @1];
return overlay;
}
diff --git a/React/Views/RCTMap.h b/React/Views/RCTMap.h
index 63e01473e..e7a076810 100644
--- a/React/Views/RCTMap.h
+++ b/React/Views/RCTMap.h
@@ -30,6 +30,7 @@ RCT_EXTERN const CGFloat RCTMapZoomBoundBuffer;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
+@property (nonatomic, copy) RCTBubblingEventBlock onAnnotationDragStateChange;
- (void)setAnnotations:(NSArray *)annotations;
- (void)setOverlays:(NSArray *)overlays;
diff --git a/React/Views/RCTMapAnnotation.h b/React/Views/RCTMapAnnotation.h
index eb03ebaf9..ec5b4edc3 100644
--- a/React/Views/RCTMapAnnotation.h
+++ b/React/Views/RCTMapAnnotation.h
@@ -21,5 +21,6 @@
@property (nonatomic, assign) NSInteger leftCalloutViewIndex;
@property (nonatomic, assign) NSInteger rightCalloutViewIndex;
@property (nonatomic, assign) NSInteger detailCalloutViewIndex;
+@property (nonatomic, assign) BOOL draggable;
@end
diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m
index 6718adde1..f12705b28 100644
--- a/React/Views/RCTMapManager.m
+++ b/React/Views/RCTMapManager.m
@@ -95,6 +95,7 @@ RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType)
RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray)
RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray)
+RCT_EXPORT_VIEW_PROPERTY(onAnnotationDragStateChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
@@ -136,7 +137,6 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if (mapView.onPress && [view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
-
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
mapView.onPress(@{
@"action": @"annotation-click",
@@ -151,6 +151,30 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
}
+- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view
+ didChangeDragState:(MKAnnotationViewDragState)newState
+ fromOldState:(MKAnnotationViewDragState)oldState
+{
+ static NSArray *states;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ states = @[@"idle", @"starting", @"dragging", @"canceling", @"ending"];
+ });
+
+ if ([view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
+ RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
+ if (mapView.onAnnotationDragStateChange) {
+ mapView.onAnnotationDragStateChange(@{
+ @"state": states[newState],
+ @"oldState": states[oldState],
+ @"annotationId": annotation.identifier,
+ @"latitude": @(annotation.coordinate.latitude),
+ @"longitude": @(annotation.coordinate.longitude),
+ });
+ }
+ }
+}
+
- (MKAnnotationView *)mapView:(RCTMap *)mapView
viewForAnnotation:(RCTMapAnnotation *)annotation
{
@@ -159,7 +183,6 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
MKAnnotationView *annotationView;
- annotationView.clipsToBounds = YES;
if (annotation.viewIndex != NSNotFound) {
NSString *reuseIdentifier = NSStringFromClass([RCTMapAnnotationView class]);
@@ -205,7 +228,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
annotation.tintColor ?: [MKPinAnnotationView redPinColor];
}
}
- annotationView.canShowCallout = true;
+ annotationView.canShowCallout = (annotation.title.length > 0);
if (annotation.leftCalloutViewIndex != NSNotFound) {
annotationView.leftCalloutAccessoryView =
@@ -255,6 +278,8 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
}
+ annotationView.draggable = annotation.draggable;
+
return annotationView;
}