Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,4 @@ env:
branches:
only:
- master
- /^.*-stable$/
12 changes: 12 additions & 0 deletions Examples/UIExplorer/MapViewExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,18 @@ var MapViewExample = React.createClass({
longitude: region.longitude,
latitude: region.latitude,
title: 'You Are Here',
rightCallout: {
type: "button",
onPress: function() {
console.info("You clicked me");
}
},
leftCallout: {
type: "image",
config: {
image: "http://facebook.github.io/react-native/img/opengraph.png?2"
}
}
}];
},

Expand Down
117 changes: 103 additions & 14 deletions Libraries/Components/MapView/MapView.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var deepDiffer = require('deepDiffer');
var insetsDiffer = require('insetsDiffer');
var merge = require('merge');
var requireNativeComponent = require('requireNativeComponent');
var resolveAssetSource = require('resolveAssetSource');

type Event = Object;
type MapRegion = {
Expand All @@ -35,8 +36,7 @@ type MapRegion = {
var MapView = React.createClass({
mixins: [NativeMethodsMixin],

checkAnnotationIds: function (annotations: Array<Object>) {

checkAnnotationData: function(annotations: Array<Object>) {
var newAnnotations = annotations.map(function (annotation) {
if (!annotation.id) {
// TODO: add a base64 (or similar) encoder here
Expand All @@ -50,16 +50,15 @@ var MapView = React.createClass({
annotations: newAnnotations
});
},

componentWillMount: function() {
if (this.props.annotations) {
this.checkAnnotationIds(this.props.annotations);
this.checkAnnotationData(this.props.annotations);
}
},

componentWillReceiveProps: function(nextProps: Object) {
if (nextProps.annotations) {
this.checkAnnotationIds(nextProps.annotations);
this.checkAnnotationData(nextProps.annotations);
}
},

Expand Down Expand Up @@ -141,7 +140,7 @@ var MapView = React.createClass({
* to be displayed.
*/
latitudeDelta: React.PropTypes.number.isRequired,
longitudeDelta: React.PropTypes.number.isRequired,
longitudeDelta: React.PropTypes.number.isRequired
}),

/**
Expand All @@ -166,16 +165,106 @@ var MapView = React.createClass({
subtitle: React.PropTypes.string,

/**
* Whether the Annotation has callout buttons.
* Right callout
*/
hasLeftCallout: React.PropTypes.bool,
hasRightCallout: React.PropTypes.bool,
rightCallout: React.PropTypes.shape({

/**
* Type of the callout. If image, set src in config
*/
type: React.PropTypes.oneOf([
'button',
'image'
]),

/**
* Callback for when the accessory is clicked
* Currently only works on button and not image
*/
onPress: React.PropTypes.func,

/**
* Additional config parameters to pass to the callout.
*/
config: React.PropTypes.shape({

/**
* Is being used when type == image. use the same input as for Image
*/
imageSize: React.PropTypes.shape({
/**
* Width of the image to be initialized
*/
width: React.PropTypes.number,

/**
* Height of the image to be initialized
*/
height: React.PropTypes.number
}),

/**
* Is being used when type == image. use the same input as for Image
*/
image: React.PropTypes.string,

/**
* Default Image is being used when the image has to get loaded
*/
defaultImage: React.PropTypes.shape
})
}),

/**
* Event handlers for callout buttons.
* Left callout
*/
onLeftCalloutPress: React.PropTypes.func,
onRightCalloutPress: React.PropTypes.func,
leftCallout: React.PropTypes.shape({

/**
* Type of the callout. If image, set src in config
*/
type: React.PropTypes.oneOf([
'button',
'image'
]),

/**
* Callback for when the accessory is clicked
* Currently only works on button and not image
*/
onPress: React.PropTypes.func,

/**
* Additional config parameters to pass to the callout.
*/
config: React.PropTypes.shape({

/**
* Is being used when type == image. use the same input as for Image
*/
imageSize: React.PropTypes.shape({
/**
* Width of the image to be initialized
*/
width: React.PropTypes.number,

/**
* Height of the image to be initialized
*/
height: React.PropTypes.number
}),

/**
* Is being used when type == image. use the same input as for Image
*/
image: React.PropTypes.string,

/**
* Default Image is being used when the image has to get loaded
*/
defaultImage: React.PropTypes.shape
})
}),

/**
* annotation id
Expand Down Expand Up @@ -242,9 +331,9 @@ var MapView = React.createClass({
if (annotation.id === event.nativeEvent.annotationId) {
// Pass the right function
if (event.nativeEvent.side === 'left') {
annotation.onLeftCalloutPress && annotation.onLeftCalloutPress(event.nativeEvent);
annotation.leftCallout.onPress && annotation.leftCallout.onPress(event.nativeEvent);
} else if (event.nativeEvent.side === 'right') {
annotation.onRightCalloutPress && annotation.onRightCalloutPress(event.nativeEvent);
annotation.rightCallout.onPress && annotation.rightCallout.onPress(event.nativeEvent);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion React.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "React"
s.version = "0.8.0"
s.version = "0.14.0"
s.summary = "Build high quality mobile apps using React."
s.description = <<-DESC
React Native apps are built using the React JS
Expand Down
15 changes: 11 additions & 4 deletions React/Base/RCTBatchedBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,20 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad
// Force JS __DEV__ value to match RCT_DEBUG
if (shouldOverrideDev) {
NSString *sourceString = [[NSString alloc] initWithData:source encoding:NSUTF8StringEncoding];
NSRange range = [sourceString rangeOfString:@"__DEV__="];
NSRange range = [sourceString rangeOfString:@"\\b__DEV__\\s*?=\\s*?(!1|!0|false|true)"
options:NSRegularExpressionSearch];

RCTAssert(range.location != NSNotFound, @"It looks like the implementation"
"of __DEV__ has changed. Update -[RCTBatchedBridge loadSource:].");
NSRange valueRange = {range.location + range.length, 2};
if ([[sourceString substringWithRange:valueRange] isEqualToString:@"!1"]) {
source = [[sourceString stringByReplacingCharactersInRange:valueRange withString:@" 1"] dataUsingEncoding:NSUTF8StringEncoding];

NSString *valueString = [sourceString substringWithRange:range];
if ([valueString rangeOfString:@"!1"].length) {
valueString = [valueString stringByReplacingOccurrencesOfString:@"!1" withString:@"!0"];
} else if ([valueString rangeOfString:@"false"].length) {
valueString = [valueString stringByReplacingOccurrencesOfString:@"false" withString:@"true"];
}
source = [[sourceString stringByReplacingCharactersInRange:range withString:valueString]
dataUsingEncoding:NSUTF8StringEncoding];
}

_onSourceLoad(error, source);
Expand Down
13 changes: 13 additions & 0 deletions React/Modules/RCTPointAnnotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,24 @@

#import <MapKit/MapKit.h>

typedef enum{
RCTPointAnnotationTypeButton,
RCTPointAnnotationTypeImage
} RCTPointAnnotationCalloutType;

@interface RCTPointAnnotation : MKPointAnnotation <MKAnnotation>

@property (nonatomic, copy) NSString *identifier;

@property (nonatomic, assign) BOOL hasLeftCallout;
@property (nonatomic, assign) BOOL hasRightCallout;

@property (nonatomic, assign) RCTPointAnnotationCalloutType leftCalloutType;
@property (nonatomic, assign) RCTPointAnnotationCalloutType rightCalloutType;

@property (nonatomic, copy) NSDictionary *rightCalloutConfig;
@property (nonatomic, copy) NSDictionary *leftCalloutConfig;

@property (nonatomic, assign) BOOL animateDrop;

@end
26 changes: 24 additions & 2 deletions React/Views/RCTConvert+MapKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,31 @@ + (RCTPointAnnotation *)RCTPointAnnotation:(id)json
shape.title = [RCTConvert NSString:json[@"title"]];
shape.subtitle = [RCTConvert NSString:json[@"subtitle"]];
shape.identifier = [RCTConvert NSString:json[@"id"]];
shape.hasLeftCallout = [RCTConvert BOOL:json[@"hasLeftCallout"]];
shape.hasRightCallout = [RCTConvert BOOL:json[@"hasRightCallout"]];
shape.animateDrop = [RCTConvert BOOL:json[@"animateDrop"]];

shape.leftCalloutConfig = [RCTConvert NSDictionary:json[@"leftCallout"][@"config"]];
shape.rightCalloutConfig = [RCTConvert NSDictionary:json[@"rightCallout"][@"config"]];

shape.hasLeftCallout = false;
if ([[RCTConvert NSDictionary:json[@"leftCallout"]] count] > 0) {
shape.hasLeftCallout = true;
}

shape.hasRightCallout = false;
if ([[RCTConvert NSDictionary:json[@"rightCallout"]] count] > 0) {
shape.hasRightCallout = true;
}

shape.leftCalloutType = RCTPointAnnotationTypeButton;
if ([[RCTConvert NSString:json[@"leftCallout"][@"type"]] isEqual: @"image"]) {
shape.leftCalloutType = RCTPointAnnotationTypeImage;
}

shape.rightCalloutType = RCTPointAnnotationTypeButton;
if ([[RCTConvert NSString:json[@"rightCallout"][@"type"]] isEqual: @"image"]) {
shape.rightCalloutType = RCTPointAnnotationTypeImage;
}

return shape;
}

Expand Down
81 changes: 74 additions & 7 deletions React/Views/RCTMapManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,66 @@ - (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)vi
}
}

- (bool)isLocalImage: (NSDictionary *)config
{
if (config[@"image"] != nil) {
if ([config[@"image"] isKindOfClass:[NSDictionary class]]) {
if ([config[@"image"] objectForKey:@"__packager_asset"] != nil) {
return true;
}
}
}

return false;
}

- (UIImageView *)generateCalloutImageAccessory:(NSDictionary *)config
{
// Default width / height is 54
int imageWidth = 54;
int imageHeight = 54;

if (config[@"imageSize"] != nil) {
if ([config[@"imageSize"] objectForKey:@"height"] != nil) {
imageHeight = [RCTConvert int:config[@"imageSize"][@"height"]];
}

if ([config[@"imageSize"] objectForKey:@"width"] != nil) {
imageWidth = [RCTConvert int:config[@"imageSize"][@"width"]];
}
}

UIGraphicsBeginImageContextWithOptions(CGSizeMake(imageWidth, imageHeight), NO, 0.0);
UIImage *blank = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

UIImageView *calloutImageView = [[UIImageView alloc] initWithImage:blank];

if ([self isLocalImage:config]) {
// We have a local ressource, no web loading necessary
NSString *ressourceName = [RCTConvert NSString:config[@"image"][@"uri"]];
calloutImageView.image = [UIImage imageNamed:ressourceName];
} else {
NSString *uri = [RCTConvert NSString:config[@"image"]];

if (config[@"defaultImage"] != nil) {
NSString *defaultImagePath = [RCTConvert NSString:config[@"defaultImage"][@"uri"]];
calloutImageView.image = [UIImage imageNamed:defaultImagePath];
}

// Load the real image async and replace the image with it
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:uri]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
if ([@[@200, @301, @302, @304] containsObject:@([(NSHTTPURLResponse *)response statusCode])]) {
calloutImageView.image = [UIImage imageWithData:data];
}
}
}];
}

return calloutImageView;
}

- (MKAnnotationView *)mapView:(__unused MKMapView *)mapView viewForAnnotation:(RCTPointAnnotation *)annotation
{
if (![annotation isKindOfClass:[RCTPointAnnotation class]]) {
Expand All @@ -84,14 +144,20 @@ - (MKAnnotationView *)mapView:(__unused MKMapView *)mapView viewForAnnotation:(R
annotationView.canShowCallout = true;
annotationView.animatesDrop = annotation.animateDrop;

annotationView.leftCalloutAccessoryView = nil;
if (annotation.hasLeftCallout) {
annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
for (NSString *side in @[@"left", @"right"]) {
NSString *accessoryViewSelector = [NSString stringWithFormat:@"%@CalloutAccessoryView", side];
NSString *typeSelector = [NSString stringWithFormat:@"%@CalloutType", side];
NSString *hasCheckSelector = [NSString stringWithFormat:@"has%@Callout", [side capitalizedString]];
NSString *configSelector = [NSString stringWithFormat:@"%@CalloutConfig", side];

[annotationView setValue:nil forKey:accessoryViewSelector];
if ([annotation valueForKey:hasCheckSelector]) {
[annotationView setValue:[UIButton buttonWithType:UIButtonTypeDetailDisclosure] forKey:accessoryViewSelector];

annotationView.rightCalloutAccessoryView = nil;
if (annotation.hasRightCallout) {
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if ([[annotation valueForKey:typeSelector] integerValue] == RCTPointAnnotationTypeImage) {
[annotationView setValue:[self generateCalloutImageAccessory:[annotation valueForKey:configSelector]] forKey:accessoryViewSelector];
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@machard good point and thanks for the comment. But since this PR is 'on hold', I am not sure whether it's worth changing the code now or just wait for react components in callouts

}

return annotationView;
Expand Down Expand Up @@ -158,6 +224,7 @@ - (void)mapViewWillStartRenderingMap:(RCTMap *)mapView
[self _emitRegionChangeEvent:mapView continuous:NO];
}


#pragma mark Private

- (void)_onTick:(NSTimer *)timer
Expand Down
2 changes: 1 addition & 1 deletion ReactAndroid/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=0.12.0-SNAPSHOT
VERSION_NAME=0.14.0
GROUP=com.facebook.react

POM_NAME=ReactNative
Expand Down
Loading