2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-03-10 01:05:10 +00:00
|
|
|
|
|
|
|
#import "RCTMapManager.h"
|
|
|
|
|
|
|
|
#import "RCTBridge.h"
|
2015-04-15 00:51:28 +00:00
|
|
|
#import "RCTConvert+CoreLocation.h"
|
|
|
|
#import "RCTConvert+MapKit.h"
|
2015-03-10 01:05:10 +00:00
|
|
|
#import "RCTEventDispatcher.h"
|
|
|
|
#import "RCTMap.h"
|
2015-11-26 11:04:33 +00:00
|
|
|
#import "RCTUtils.h"
|
2015-03-26 09:58:06 +00:00
|
|
|
#import "UIView+React.h"
|
Add Polyline support to MapView
Summary: Per issue #1925, add support for Polyline to MapView.
Briefly, if you have a MapView declared as:
<MapView
annotations={this.state.annotations}
overlays={this.state.overlays}
style={styles.map}
region={this.state.region}
ref="mapView"
/>
then setting
this.state.overlays = [{
coordinates: [
{ latitude: 35.5, longitude: -5.5 },
{ latitude: 35.6, longitude: -5.6 },
...
],
strokeColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 3,
}];
will draw a red line between the points in locations with a width of 3 and equally blended with the background.
Closes https://github.com/facebook/react-native/pull/4153
Reviewed By: svcscm
Differential Revision: D2697347
Pulled By: nicklockwood
fb-gh-sync-id: a436e4ed8d4e43f2872b39b4694fad7c02de8fe5
2015-11-26 15:09:59 +00:00
|
|
|
#import "RCTMapAnnotation.h"
|
|
|
|
#import "RCTMapOverlay.h"
|
2015-06-25 16:07:19 +00:00
|
|
|
|
|
|
|
#import <MapKit/MapKit.h>
|
2015-03-10 01:05:10 +00:00
|
|
|
|
2015-04-15 00:51:28 +00:00
|
|
|
static NSString *const RCTMapViewKey = @"MapView";
|
2015-03-26 04:29:28 +00:00
|
|
|
|
2015-11-30 13:07:43 +00:00
|
|
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
|
|
|
|
|
|
|
|
static NSString *const RCTMapPinRed = @"#ff3b30";
|
|
|
|
static NSString *const RCTMapPinGreen = @"#4cd964";
|
|
|
|
static NSString *const RCTMapPinPurple = @"#c969e0";
|
|
|
|
|
|
|
|
@implementation RCTConvert (MKPinAnnotationColor)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(MKPinAnnotationColor, (@{
|
|
|
|
RCTMapPinRed: @(MKPinAnnotationColorRed),
|
|
|
|
RCTMapPinGreen: @(MKPinAnnotationColorGreen),
|
|
|
|
RCTMapPinPurple: @(MKPinAnnotationColorPurple)
|
|
|
|
}), MKPinAnnotationColorRed, unsignedIntegerValue)
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2015-12-19 17:15:38 +00:00
|
|
|
@interface RCTMapAnnotationView : MKAnnotationView
|
|
|
|
|
|
|
|
@property (nonatomic, strong) UIView *contentView;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RCTMapAnnotationView
|
|
|
|
|
|
|
|
- (void)setContentView:(UIView *)contentView
|
|
|
|
{
|
|
|
|
[_contentView removeFromSuperview];
|
|
|
|
_contentView = contentView;
|
|
|
|
[self addSubview:_contentView];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)layoutSubviews
|
|
|
|
{
|
|
|
|
[super layoutSubviews];
|
|
|
|
self.bounds = (CGRect){
|
|
|
|
CGPointZero,
|
|
|
|
_contentView.frame.size,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2015-03-10 01:05:10 +00:00
|
|
|
@interface RCTMapManager() <MKMapViewDelegate>
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RCTMapManager
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2015-03-10 01:05:10 +00:00
|
|
|
- (UIView *)view
|
|
|
|
{
|
2015-08-17 14:35:34 +00:00
|
|
|
RCTMap *map = [RCTMap new];
|
2015-03-10 01:05:10 +00:00
|
|
|
map.delegate = self;
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL)
|
2015-10-29 17:56:27 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(showsPointsOfInterest, BOOL)
|
2015-11-19 10:20:23 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(showsCompass, BOOL)
|
2016-11-20 08:55:22 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(showsAnnotationCallouts, BOOL)
|
2016-01-06 19:00:15 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(followUserLocation, BOOL)
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(rotateEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(maxDelta, CGFloat)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets)
|
2015-06-11 17:46:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType)
|
2015-12-17 14:45:53 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray<RCTMapAnnotation *>)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray<RCTMapOverlay *>)
|
2016-01-04 14:37:15 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onAnnotationDragStateChange, RCTBubblingEventBlock)
|
2016-01-29 14:25:35 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onAnnotationFocus, RCTBubblingEventBlock)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onAnnotationBlur, RCTBubblingEventBlock)
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
|
2015-04-16 12:29:06 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
|
|
|
|
{
|
2017-01-10 03:23:44 +00:00
|
|
|
#pragma unused (defaultView)
|
2016-08-16 17:50:39 +00:00
|
|
|
if (json) {
|
|
|
|
[view setRegion:[RCTConvert MKCoordinateRegion:json] animated:YES];
|
|
|
|
}
|
2015-04-16 12:29:06 +00:00
|
|
|
}
|
2015-03-10 01:05:10 +00:00
|
|
|
|
|
|
|
#pragma mark MKMapViewDelegate
|
|
|
|
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view
|
2015-06-25 16:07:19 +00:00
|
|
|
{
|
2016-01-29 14:25:35 +00:00
|
|
|
// TODO: Remove deprecated onAnnotationPress API call later.
|
Add Polyline support to MapView
Summary: Per issue #1925, add support for Polyline to MapView.
Briefly, if you have a MapView declared as:
<MapView
annotations={this.state.annotations}
overlays={this.state.overlays}
style={styles.map}
region={this.state.region}
ref="mapView"
/>
then setting
this.state.overlays = [{
coordinates: [
{ latitude: 35.5, longitude: -5.5 },
{ latitude: 35.6, longitude: -5.6 },
...
],
strokeColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 3,
}];
will draw a red line between the points in locations with a width of 3 and equally blended with the background.
Closes https://github.com/facebook/react-native/pull/4153
Reviewed By: svcscm
Differential Revision: D2697347
Pulled By: nicklockwood
fb-gh-sync-id: a436e4ed8d4e43f2872b39b4694fad7c02de8fe5
2015-11-26 15:09:59 +00:00
|
|
|
if (mapView.onPress && [view.annotation isKindOfClass:[RCTMapAnnotation class]]) {
|
|
|
|
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
mapView.onPress(@{
|
|
|
|
@"action": @"annotation-click",
|
|
|
|
@"annotation": @{
|
|
|
|
@"id": annotation.identifier,
|
|
|
|
@"title": annotation.title ?: @"",
|
|
|
|
@"subtitle": annotation.subtitle ?: @"",
|
|
|
|
@"latitude": @(annotation.coordinate.latitude),
|
|
|
|
@"longitude": @(annotation.coordinate.longitude)
|
|
|
|
}
|
|
|
|
});
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
2016-01-29 14:25:35 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 13:19:45 +00:00
|
|
|
#if !TARGET_OS_TV
|
2016-01-04 14:37:15 +00:00
|
|
|
- (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),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-27 13:19:45 +00:00
|
|
|
#endif //TARGET_OS_TV
|
2016-01-04 14:37:15 +00:00
|
|
|
|
2015-12-17 14:45:53 +00:00
|
|
|
- (MKAnnotationView *)mapView:(RCTMap *)mapView
|
|
|
|
viewForAnnotation:(RCTMapAnnotation *)annotation
|
2015-06-25 16:07:19 +00:00
|
|
|
{
|
Add Polyline support to MapView
Summary: Per issue #1925, add support for Polyline to MapView.
Briefly, if you have a MapView declared as:
<MapView
annotations={this.state.annotations}
overlays={this.state.overlays}
style={styles.map}
region={this.state.region}
ref="mapView"
/>
then setting
this.state.overlays = [{
coordinates: [
{ latitude: 35.5, longitude: -5.5 },
{ latitude: 35.6, longitude: -5.6 },
...
],
strokeColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 3,
}];
will draw a red line between the points in locations with a width of 3 and equally blended with the background.
Closes https://github.com/facebook/react-native/pull/4153
Reviewed By: svcscm
Differential Revision: D2697347
Pulled By: nicklockwood
fb-gh-sync-id: a436e4ed8d4e43f2872b39b4694fad7c02de8fe5
2015-11-26 15:09:59 +00:00
|
|
|
if (![annotation isKindOfClass:[RCTMapAnnotation class]]) {
|
2015-06-25 16:07:19 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-11-26 11:04:33 +00:00
|
|
|
MKAnnotationView *annotationView;
|
2016-04-22 17:58:48 +00:00
|
|
|
if (annotation.viewIndex != NSNotFound &&
|
|
|
|
annotation.viewIndex < mapView.reactSubviews.count) {
|
2015-12-17 14:45:53 +00:00
|
|
|
|
2015-12-19 17:15:38 +00:00
|
|
|
NSString *reuseIdentifier = NSStringFromClass([RCTMapAnnotationView class]);
|
2015-12-17 14:45:53 +00:00
|
|
|
annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
|
|
|
|
if (!annotationView) {
|
2015-12-19 17:15:38 +00:00
|
|
|
annotationView = [[RCTMapAnnotationView alloc] initWithAnnotation:annotation
|
|
|
|
reuseIdentifier:reuseIdentifier];
|
2015-12-17 14:45:53 +00:00
|
|
|
}
|
|
|
|
UIView *reactView = mapView.reactSubviews[annotation.viewIndex];
|
2015-12-19 17:15:38 +00:00
|
|
|
((RCTMapAnnotationView *)annotationView).contentView = reactView;
|
2015-11-26 11:04:33 +00:00
|
|
|
|
2015-12-17 14:45:53 +00:00
|
|
|
} else if (annotation.image) {
|
2015-06-25 16:07:19 +00:00
|
|
|
|
2015-12-17 14:45:53 +00:00
|
|
|
NSString *reuseIdentifier = NSStringFromClass([MKAnnotationView class]);
|
|
|
|
annotationView =
|
|
|
|
[mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier] ?:
|
|
|
|
[[MKAnnotationView alloc] initWithAnnotation:annotation
|
|
|
|
reuseIdentifier:reuseIdentifier];
|
|
|
|
annotationView.image = annotation.image;
|
2015-11-26 11:04:33 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
NSString *reuseIdentifier = NSStringFromClass([MKPinAnnotationView class]);
|
2015-12-17 14:45:53 +00:00
|
|
|
annotationView =
|
|
|
|
[mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier] ?:
|
|
|
|
[[MKPinAnnotationView alloc] initWithAnnotation:annotation
|
|
|
|
reuseIdentifier:reuseIdentifier];
|
2015-11-26 11:04:33 +00:00
|
|
|
((MKPinAnnotationView *)annotationView).animatesDrop = annotation.animateDrop;
|
2015-11-30 13:07:43 +00:00
|
|
|
|
|
|
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
|
|
|
|
|
|
|
|
if (![annotationView respondsToSelector:@selector(pinTintColor)]) {
|
2015-12-17 14:45:53 +00:00
|
|
|
NSString *hexColor = annotation.tintColor ?
|
|
|
|
RCTColorToHexString(annotation.tintColor.CGColor) : RCTMapPinRed;
|
|
|
|
((MKPinAnnotationView *)annotationView).pinColor =
|
|
|
|
[RCTConvert MKPinAnnotationColor:hexColor];
|
2015-11-30 13:07:43 +00:00
|
|
|
} else
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{
|
2015-12-17 14:45:53 +00:00
|
|
|
((MKPinAnnotationView *)annotationView).pinTintColor =
|
|
|
|
annotation.tintColor ?: [MKPinAnnotationView redPinColor];
|
2015-11-30 13:07:43 +00:00
|
|
|
}
|
2015-11-26 11:04:33 +00:00
|
|
|
}
|
2016-01-04 14:37:15 +00:00
|
|
|
annotationView.canShowCallout = (annotation.title.length > 0);
|
2015-06-25 16:07:19 +00:00
|
|
|
|
2016-04-19 12:02:11 +00:00
|
|
|
if (annotation.leftCalloutViewIndex != NSNotFound &&
|
|
|
|
annotation.leftCalloutViewIndex < mapView.reactSubviews.count) {
|
2015-12-17 14:45:53 +00:00
|
|
|
annotationView.leftCalloutAccessoryView =
|
|
|
|
mapView.reactSubviews[annotation.leftCalloutViewIndex];
|
|
|
|
} else if (annotation.hasLeftCallout) {
|
|
|
|
annotationView.leftCalloutAccessoryView =
|
|
|
|
[UIButton buttonWithType:UIButtonTypeDetailDisclosure];
|
|
|
|
} else {
|
|
|
|
annotationView.leftCalloutAccessoryView = nil;
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 12:02:11 +00:00
|
|
|
if (annotation.rightCalloutViewIndex != NSNotFound &&
|
|
|
|
annotation.rightCalloutViewIndex < mapView.reactSubviews.count) {
|
2015-12-17 14:45:53 +00:00
|
|
|
annotationView.rightCalloutAccessoryView =
|
|
|
|
mapView.reactSubviews[annotation.rightCalloutViewIndex];
|
|
|
|
} else if (annotation.hasRightCallout) {
|
|
|
|
annotationView.rightCalloutAccessoryView =
|
|
|
|
[UIButton buttonWithType:UIButtonTypeDetailDisclosure];
|
|
|
|
} else {
|
|
|
|
annotationView.rightCalloutAccessoryView = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//http://stackoverflow.com/questions/32581049/mapkit-ios-9-detailcalloutaccessoryview-usage
|
|
|
|
if ([annotationView respondsToSelector:@selector(detailCalloutAccessoryView)]) {
|
2016-04-19 12:02:11 +00:00
|
|
|
if (annotation.detailCalloutViewIndex != NSNotFound &&
|
|
|
|
annotation.detailCalloutViewIndex < mapView.reactSubviews.count) {
|
2015-12-17 14:45:53 +00:00
|
|
|
UIView *calloutView = mapView.reactSubviews[annotation.detailCalloutViewIndex];
|
|
|
|
NSLayoutConstraint *widthConstraint =
|
|
|
|
[NSLayoutConstraint constraintWithItem:calloutView
|
|
|
|
attribute:NSLayoutAttributeWidth
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:nil
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
multiplier:1
|
|
|
|
constant:calloutView.frame.size.width];
|
|
|
|
[calloutView addConstraint:widthConstraint];
|
|
|
|
NSLayoutConstraint *heightConstraint =
|
|
|
|
[NSLayoutConstraint constraintWithItem:calloutView
|
|
|
|
attribute:NSLayoutAttributeHeight
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:nil
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
multiplier:1
|
|
|
|
constant:calloutView.frame.size.height];
|
|
|
|
[calloutView addConstraint:heightConstraint];
|
|
|
|
annotationView.detailCalloutAccessoryView = calloutView;
|
|
|
|
} else {
|
|
|
|
annotationView.detailCalloutAccessoryView = nil;
|
|
|
|
}
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 13:19:45 +00:00
|
|
|
#if !TARGET_OS_TV
|
2016-01-04 14:37:15 +00:00
|
|
|
annotationView.draggable = annotation.draggable;
|
2016-09-27 13:19:45 +00:00
|
|
|
#endif
|
2016-01-04 14:37:15 +00:00
|
|
|
|
2015-06-25 16:07:19 +00:00
|
|
|
return annotationView;
|
|
|
|
}
|
|
|
|
|
2017-01-10 03:23:44 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView didAddAnnotationViews:(__unused NSArray *)views {
|
2016-11-20 08:55:22 +00:00
|
|
|
if (mapView.showsAnnotationCallouts) {
|
|
|
|
for (id<MKAnnotation> annotation in mapView.annotations) {
|
|
|
|
[mapView selectAnnotation:annotation animated:YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-10 03:23:44 +00:00
|
|
|
- (MKOverlayRenderer *)mapView:(__unused MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
|
Add Polyline support to MapView
Summary: Per issue #1925, add support for Polyline to MapView.
Briefly, if you have a MapView declared as:
<MapView
annotations={this.state.annotations}
overlays={this.state.overlays}
style={styles.map}
region={this.state.region}
ref="mapView"
/>
then setting
this.state.overlays = [{
coordinates: [
{ latitude: 35.5, longitude: -5.5 },
{ latitude: 35.6, longitude: -5.6 },
...
],
strokeColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 3,
}];
will draw a red line between the points in locations with a width of 3 and equally blended with the background.
Closes https://github.com/facebook/react-native/pull/4153
Reviewed By: svcscm
Differential Revision: D2697347
Pulled By: nicklockwood
fb-gh-sync-id: a436e4ed8d4e43f2872b39b4694fad7c02de8fe5
2015-11-26 15:09:59 +00:00
|
|
|
{
|
2016-10-27 11:17:43 +00:00
|
|
|
RCTAssert([overlay isKindOfClass:[RCTMapOverlay class]], @"Overlay must be of type RCTMapOverlay");
|
|
|
|
MKPolylineRenderer *polylineRenderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
|
|
|
|
polylineRenderer.strokeColor = [(RCTMapOverlay *)overlay strokeColor];
|
|
|
|
polylineRenderer.lineWidth = [(RCTMapOverlay *)overlay lineWidth];
|
|
|
|
return polylineRenderer;
|
Add Polyline support to MapView
Summary: Per issue #1925, add support for Polyline to MapView.
Briefly, if you have a MapView declared as:
<MapView
annotations={this.state.annotations}
overlays={this.state.overlays}
style={styles.map}
region={this.state.region}
ref="mapView"
/>
then setting
this.state.overlays = [{
coordinates: [
{ latitude: 35.5, longitude: -5.5 },
{ latitude: 35.6, longitude: -5.6 },
...
],
strokeColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 3,
}];
will draw a red line between the points in locations with a width of 3 and equally blended with the background.
Closes https://github.com/facebook/react-native/pull/4153
Reviewed By: svcscm
Differential Revision: D2697347
Pulled By: nicklockwood
fb-gh-sync-id: a436e4ed8d4e43f2872b39b4694fad7c02de8fe5
2015-11-26 15:09:59 +00:00
|
|
|
}
|
|
|
|
|
2015-12-17 14:45:53 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view
|
|
|
|
calloutAccessoryControlTapped:(UIControl *)control
|
2015-06-25 16:07:19 +00:00
|
|
|
{
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
if (mapView.onPress) {
|
2015-12-17 14:45:53 +00:00
|
|
|
// Pass to JS
|
Add Polyline support to MapView
Summary: Per issue #1925, add support for Polyline to MapView.
Briefly, if you have a MapView declared as:
<MapView
annotations={this.state.annotations}
overlays={this.state.overlays}
style={styles.map}
region={this.state.region}
ref="mapView"
/>
then setting
this.state.overlays = [{
coordinates: [
{ latitude: 35.5, longitude: -5.5 },
{ latitude: 35.6, longitude: -5.6 },
...
],
strokeColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 3,
}];
will draw a red line between the points in locations with a width of 3 and equally blended with the background.
Closes https://github.com/facebook/react-native/pull/4153
Reviewed By: svcscm
Differential Revision: D2697347
Pulled By: nicklockwood
fb-gh-sync-id: a436e4ed8d4e43f2872b39b4694fad7c02de8fe5
2015-11-26 15:09:59 +00:00
|
|
|
RCTMapAnnotation *annotation = (RCTMapAnnotation *)view.annotation;
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
mapView.onPress(@{
|
|
|
|
@"side": (control == view.leftCalloutAccessoryView) ? @"left" : @"right",
|
2015-06-25 16:07:19 +00:00
|
|
|
@"action": @"callout-click",
|
|
|
|
@"annotationId": annotation.identifier
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
});
|
|
|
|
}
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-03-10 01:05:10 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView didUpdateUserLocation:(MKUserLocation *)location
|
|
|
|
{
|
|
|
|
if (mapView.followUserLocation) {
|
|
|
|
MKCoordinateRegion region;
|
|
|
|
region.span.latitudeDelta = RCTMapDefaultSpan;
|
|
|
|
region.span.longitudeDelta = RCTMapDefaultSpan;
|
|
|
|
region.center = location.coordinate;
|
|
|
|
[mapView setRegion:region animated:YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView regionWillChangeAnimated:(__unused BOOL)animated
|
2015-03-10 01:05:10 +00:00
|
|
|
{
|
|
|
|
[self _regionChanged:mapView];
|
|
|
|
|
2015-12-17 14:45:53 +00:00
|
|
|
mapView.regionChangeObserveTimer =
|
|
|
|
[NSTimer timerWithTimeInterval:RCTMapRegionChangeObserveInterval
|
|
|
|
target:self
|
|
|
|
selector:@selector(_onTick:)
|
|
|
|
userInfo:@{ RCTMapViewKey: mapView }
|
|
|
|
repeats:YES];
|
2015-04-21 17:19:18 +00:00
|
|
|
|
2015-12-17 14:45:53 +00:00
|
|
|
[[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer
|
|
|
|
forMode:NSRunLoopCommonModes];
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(__unused BOOL)animated
|
2015-03-10 01:05:10 +00:00
|
|
|
{
|
|
|
|
[mapView.regionChangeObserveTimer invalidate];
|
|
|
|
mapView.regionChangeObserveTimer = nil;
|
2015-03-13 16:10:57 +00:00
|
|
|
|
|
|
|
[self _regionChanged:mapView];
|
2015-04-15 00:51:28 +00:00
|
|
|
|
|
|
|
// Don't send region did change events until map has
|
2015-05-07 23:20:08 +00:00
|
|
|
// started rendering, as these won't represent the final location
|
|
|
|
if (mapView.hasStartedRendering) {
|
2015-04-15 00:51:28 +00:00
|
|
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-05-07 23:20:08 +00:00
|
|
|
- (void)mapViewWillStartRenderingMap:(RCTMap *)mapView
|
2015-04-15 00:51:28 +00:00
|
|
|
{
|
2015-05-07 23:20:08 +00:00
|
|
|
mapView.hasStartedRendering = YES;
|
2015-03-13 16:10:57 +00:00
|
|
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark Private
|
|
|
|
|
|
|
|
- (void)_onTick:(NSTimer *)timer
|
|
|
|
{
|
2015-04-15 00:51:28 +00:00
|
|
|
[self _regionChanged:timer.userInfo[RCTMapViewKey]];
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_regionChanged:(RCTMap *)mapView
|
|
|
|
{
|
|
|
|
BOOL needZoom = NO;
|
|
|
|
CGFloat newLongitudeDelta = 0.0f;
|
|
|
|
MKCoordinateRegion region = mapView.region;
|
2015-11-26 11:04:33 +00:00
|
|
|
|
|
|
|
// On iOS 7, it's possible that we observe invalid locations during
|
|
|
|
// initialization of the map. Filter those out.
|
2015-03-10 01:05:10 +00:00
|
|
|
if (!CLLocationCoordinate2DIsValid(region.center)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-11-26 11:04:33 +00:00
|
|
|
|
|
|
|
// Calculation on float is not 100% accurate. If user zoom to max/min and then
|
|
|
|
// move, it's likely the map will auto zoom to max/min from time to time.
|
|
|
|
// So let's try to make map zoom back to 99% max or 101% min so that there is
|
|
|
|
// some buffer, and moving the map won't constantly hit the max/min bound.
|
2015-12-17 14:45:53 +00:00
|
|
|
if (mapView.maxDelta > FLT_EPSILON &&
|
|
|
|
region.span.longitudeDelta > mapView.maxDelta) {
|
2015-03-10 01:05:10 +00:00
|
|
|
needZoom = YES;
|
|
|
|
newLongitudeDelta = mapView.maxDelta * (1 - RCTMapZoomBoundBuffer);
|
2015-12-17 14:45:53 +00:00
|
|
|
} else if (mapView.minDelta > FLT_EPSILON &&
|
|
|
|
region.span.longitudeDelta < mapView.minDelta) {
|
2015-03-10 01:05:10 +00:00
|
|
|
needZoom = YES;
|
|
|
|
newLongitudeDelta = mapView.minDelta * (1 + RCTMapZoomBoundBuffer);
|
|
|
|
}
|
|
|
|
if (needZoom) {
|
2015-12-17 14:45:53 +00:00
|
|
|
region.span.latitudeDelta =
|
|
|
|
region.span.latitudeDelta / region.span.longitudeDelta * newLongitudeDelta;
|
2015-03-10 01:05:10 +00:00
|
|
|
region.span.longitudeDelta = newLongitudeDelta;
|
|
|
|
mapView.region = region;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Continously observe region changes
|
|
|
|
[self _emitRegionChangeEvent:mapView continuous:YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_emitRegionChangeEvent:(RCTMap *)mapView continuous:(BOOL)continuous
|
|
|
|
{
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
if (mapView.onChange) {
|
|
|
|
MKCoordinateRegion region = mapView.region;
|
|
|
|
if (!CLLocationCoordinate2DIsValid(region.center)) {
|
|
|
|
return;
|
2015-03-26 04:29:28 +00:00
|
|
|
}
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
|
|
|
|
mapView.onChange(@{
|
|
|
|
@"continuous": @(continuous),
|
|
|
|
@"region": @{
|
2015-11-26 11:04:33 +00:00
|
|
|
@"latitude": @(RCTZeroIfNaN(region.center.latitude)),
|
|
|
|
@"longitude": @(RCTZeroIfNaN(region.center.longitude)),
|
|
|
|
@"latitudeDelta": @(RCTZeroIfNaN(region.span.latitudeDelta)),
|
|
|
|
@"longitudeDelta": @(RCTZeroIfNaN(region.span.longitudeDelta)),
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|