Updates from Fri 26, Jun

This commit is contained in:
Alex Kotliarskyi 2015-06-26 15:16:59 -07:00
commit 843e181542
25 changed files with 396 additions and 156 deletions

View File

@ -66,6 +66,7 @@ var NavigatorIOSColors = React.createClass({
tintColor="#FFFFFF" tintColor="#FFFFFF"
barTintColor="#183E63" barTintColor="#183E63"
titleTextColor="#FFFFFF" titleTextColor="#FFFFFF"
translucent="true"
/> />
); );
}, },

View File

@ -23,6 +23,7 @@ var TypesEnum = {
easeInEaseOut: true, easeInEaseOut: true,
easeIn: true, easeIn: true,
easeOut: true, easeOut: true,
keyboard: true,
}; };
var Types = keyMirror(TypesEnum); var Types = keyMirror(TypesEnum);
@ -113,4 +114,10 @@ var LayoutAnimation = {
} }
}; };
for (var key in LayoutAnimation.Presets) {
LayoutAnimation[key] = LayoutAnimation.configureNext.bind(
null, LayoutAnimation.Presets[key]
);
}
module.exports = LayoutAnimation; module.exports = LayoutAnimation;

View File

@ -35,6 +35,34 @@ type MapRegion = {
var MapView = React.createClass({ var MapView = React.createClass({
mixins: [NativeMethodsMixin], mixins: [NativeMethodsMixin],
checkAnnotationIds: function (annotations: Array<Object>) {
var newAnnotations = annotations.map(function (annotation) {
if (!annotation.id) {
// TODO: add a base64 (or similar) encoder here
annotation.id = encodeURIComponent(JSON.stringify(annotation));
}
return annotation;
});
this.setState({
annotations: newAnnotations
});
},
componentWillMount: function() {
if (this.props.annotations) {
this.checkAnnotationIds(this.props.annotations);
}
},
componentWillReceiveProps: function(nextProps: Object) {
if (nextProps.annotations) {
this.checkAnnotationIds(nextProps.annotations);
}
},
propTypes: { propTypes: {
/** /**
* Used to style and layout the `MapView`. See `StyleSheet.js` and * Used to style and layout the `MapView`. See `StyleSheet.js` and
@ -84,14 +112,14 @@ var MapView = React.createClass({
/** /**
* The map type to be displayed. * The map type to be displayed.
* *
* - standard: standard road map (default) * - standard: standard road map (default)
* - satellite: satellite view * - satellite: satellite view
* - hybrid: satellite view with roads and points of interest overlayed * - hybrid: satellite view with roads and points of interest overlayed
*/ */
mapType: React.PropTypes.oneOf([ mapType: React.PropTypes.oneOf([
'standard', 'standard',
'satellite', 'satellite',
'hybrid', 'hybrid',
]), ]),
@ -126,11 +154,34 @@ var MapView = React.createClass({
latitude: React.PropTypes.number.isRequired, latitude: React.PropTypes.number.isRequired,
longitude: React.PropTypes.number.isRequired, longitude: React.PropTypes.number.isRequired,
/**
* Whether the pin drop should be animated or not
*/
animateDrop: React.PropTypes.bool,
/** /**
* Annotation title/subtile. * Annotation title/subtile.
*/ */
title: React.PropTypes.string, title: React.PropTypes.string,
subtitle: React.PropTypes.string, subtitle: React.PropTypes.string,
/**
* Whether the Annotation has callout buttons.
*/
hasLeftCallout: React.PropTypes.bool,
hasRightCallout: React.PropTypes.bool,
/**
* Event handlers for callout buttons.
*/
onLeftCalloutPress: React.PropTypes.func,
onRightCalloutPress: React.PropTypes.func,
/**
* annotation id
*/
id: React.PropTypes.string
})), })),
/** /**
@ -158,6 +209,11 @@ var MapView = React.createClass({
* Callback that is called once, when the user is done moving the map. * Callback that is called once, when the user is done moving the map.
*/ */
onRegionChangeComplete: React.PropTypes.func, onRegionChangeComplete: React.PropTypes.func,
/**
* Callback that is called once, when the user is clicked on a annotation.
*/
onAnnotationPress: React.PropTypes.func,
}, },
_onChange: function(event: Event) { _onChange: function(event: Event) {
@ -170,8 +226,34 @@ var MapView = React.createClass({
} }
}, },
_onPress: function(event: Event) {
if (event.nativeEvent.action === 'annotation-click') {
this.props.onAnnotationPress && this.props.onAnnotationPress(event.nativeEvent.annotation);
}
if (event.nativeEvent.action === 'callout-click') {
if (!this.props.annotations) {
return;
}
// Find the annotation with the id of what has been pressed
for (var i = 0; i < this.props.annotations.length; i++) {
var annotation = this.props.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);
}
}
}
}
},
render: function() { render: function() {
return <RCTMap {...this.props} onChange={this._onChange} />; return <RCTMap {...this.props} onPress={this._onPress} onChange={this._onChange} />;
}, },
}); });
@ -179,6 +261,7 @@ if (Platform.OS === 'android') {
var RCTMap = createReactNativeComponentClass({ var RCTMap = createReactNativeComponentClass({
validAttributes: merge( validAttributes: merge(
ReactNativeViewAttributes.UIView, { ReactNativeViewAttributes.UIView, {
active: true,
showsUserLocation: true, showsUserLocation: true,
zoomEnabled: true, zoomEnabled: true,
rotateEnabled: true, rotateEnabled: true,

View File

@ -57,6 +57,7 @@ var RCTNavigatorItem = createReactNativeComponentClass({
backButtonIcon: true, backButtonIcon: true,
backButtonTitle: true, backButtonTitle: true,
tintColor: true, tintColor: true,
translucent: true,
navigationBarHidden: true, navigationBarHidden: true,
titleTextColor: true, titleTextColor: true,
style: true, style: true,
@ -300,6 +301,11 @@ var NavigatorIOS = React.createClass({
*/ */
titleTextColor: PropTypes.string, titleTextColor: PropTypes.string,
/**
* A Boolean value that indicates whether the navigation bar is translucent
*/
translucent: PropTypes.bool,
}, },
navigator: (undefined: ?Object), navigator: (undefined: ?Object),
@ -609,6 +615,7 @@ var NavigatorIOS = React.createClass({
navigationBarHidden={this.props.navigationBarHidden} navigationBarHidden={this.props.navigationBarHidden}
tintColor={this.props.tintColor} tintColor={this.props.tintColor}
barTintColor={this.props.barTintColor} barTintColor={this.props.barTintColor}
translucent={this.props.translucent}
titleTextColor={this.props.titleTextColor}> titleTextColor={this.props.titleTextColor}>
<Component <Component
navigator={this.navigator} navigator={this.navigator}

View File

@ -276,7 +276,9 @@ var ListView = React.createClass({
}, },
componentDidUpdate: function() { componentDidUpdate: function() {
this._measureAndUpdateScrollProps(); this.requestAnimationFrame(() => {
this._measureAndUpdateScrollProps();
});
}, },
onRowHighlighted: function(sectionID, rowID) { onRowHighlighted: function(sectionID, rowID) {

View File

@ -30,6 +30,7 @@
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule; var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
var Dimensions = require('Dimensions'); var Dimensions = require('Dimensions');
var InteractionMixin = require('InteractionMixin'); var InteractionMixin = require('InteractionMixin');
var Map = require('Map');
var NavigationContext = require('NavigationContext'); var NavigationContext = require('NavigationContext');
var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar'); var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
var NavigatorNavigationBar = require('NavigatorNavigationBar'); var NavigatorNavigationBar = require('NavigatorNavigationBar');
@ -257,6 +258,8 @@ var Navigator = React.createClass({
}, },
getInitialState: function() { getInitialState: function() {
this._renderedSceneMap = new Map();
var routeStack = this.props.initialRouteStack || [this.props.initialRoute]; var routeStack = this.props.initialRouteStack || [this.props.initialRoute];
invariant( invariant(
routeStack.length >= 1, routeStack.length >= 1,
@ -276,10 +279,6 @@ var Navigator = React.createClass({
), ),
idStack: routeStack.map(() => getuid()), idStack: routeStack.map(() => getuid()),
routeStack, routeStack,
// `updatingRange*` allows us to only render the visible or staged scenes
// On first render, we will render every scene in the initialRouteStack
updatingRangeStart: 0,
updatingRangeLength: routeStack.length,
presentedIndex: initialRouteIndex, presentedIndex: initialRouteIndex,
transitionFromIndex: null, transitionFromIndex: null,
activeGesture: null, activeGesture: null,
@ -351,8 +350,6 @@ var Navigator = React.createClass({
sceneConfigStack: nextRouteStack.map( sceneConfigStack: nextRouteStack.map(
this.props.configureScene this.props.configureScene
), ),
updatingRangeStart: 0,
updatingRangeLength: nextRouteStack.length,
presentedIndex: destIndex, presentedIndex: destIndex,
activeGesture: null, activeGesture: null,
transitionFromIndex: null, transitionFromIndex: null,
@ -395,7 +392,6 @@ var Navigator = React.createClass({
this.spring.getSpringConfig().tension = sceneConfig.springTension; this.spring.getSpringConfig().tension = sceneConfig.springTension;
this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity); this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity);
this.spring.setEndValue(1); this.spring.setEndValue(1);
this._emitWillFocus(this.state.routeStack[this.state.presentedIndex]);
}, },
/** /**
@ -461,6 +457,7 @@ var Navigator = React.createClass({
if (this.state.transitionQueue.length) { if (this.state.transitionQueue.length) {
var queuedTransition = this.state.transitionQueue.shift(); var queuedTransition = this.state.transitionQueue.shift();
this._enableScene(queuedTransition.destIndex); this._enableScene(queuedTransition.destIndex);
this._emitWillFocus(this.state.routeStack[queuedTransition.destIndex]);
this._transitionTo( this._transitionTo(
queuedTransition.destIndex, queuedTransition.destIndex,
queuedTransition.velocity, queuedTransition.velocity,
@ -660,6 +657,7 @@ var Navigator = React.createClass({
} }
} else { } else {
// The gesture has enough velocity to complete, so we transition to the gesture's destination // The gesture has enough velocity to complete, so we transition to the gesture's destination
this._emitWillFocus(this.state.routeStack[destIndex]);
this._transitionTo( this._transitionTo(
destIndex, destIndex,
transitionVelocity, transitionVelocity,
@ -829,11 +827,6 @@ var Navigator = React.createClass({
return false; return false;
}, },
_resetUpdatingRange: function() {
this.state.updatingRangeStart = 0;
this.state.updatingRangeLength = this.state.routeStack.length;
},
_getDestIndexWithinBounds: function(n) { _getDestIndexWithinBounds: function(n) {
var currentIndex = this.state.presentedIndex; var currentIndex = this.state.presentedIndex;
var destIndex = currentIndex + n; var destIndex = currentIndex + n;
@ -851,15 +844,9 @@ var Navigator = React.createClass({
_jumpN: function(n) { _jumpN: function(n) {
var destIndex = this._getDestIndexWithinBounds(n); var destIndex = this._getDestIndexWithinBounds(n);
var requestTransitionAndResetUpdatingRange = () => { this._enableScene(destIndex);
this._enableScene(destIndex); this._emitWillFocus(this.state.routeStack[destIndex]);
this._transitionTo(destIndex); this._transitionTo(destIndex);
this._resetUpdatingRange();
};
this.setState({
updatingRangeStart: destIndex,
updatingRangeLength: 1,
}, requestTransitionAndResetUpdatingRange);
}, },
jumpTo: function(route) { jumpTo: function(route) {
@ -891,18 +878,15 @@ var Navigator = React.createClass({
var nextAnimationConfigStack = activeAnimationConfigStack.concat([ var nextAnimationConfigStack = activeAnimationConfigStack.concat([
this.props.configureScene(route), this.props.configureScene(route),
]); ]);
var requestTransitionAndResetUpdatingRange = () => { this._emitWillFocus(nextStack[destIndex]);
this._enableScene(destIndex);
this._transitionTo(destIndex);
this._resetUpdatingRange();
};
this.setState({ this.setState({
idStack: nextIDStack, idStack: nextIDStack,
routeStack: nextStack, routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack, sceneConfigStack: nextAnimationConfigStack,
updatingRangeStart: nextStack.length - 1, }, () => {
updatingRangeLength: 1, this._enableScene(destIndex);
}, requestTransitionAndResetUpdatingRange); this._transitionTo(destIndex);
});
}, },
_popN: function(n) { _popN: function(n) {
@ -915,6 +899,7 @@ var Navigator = React.createClass({
); );
var popIndex = this.state.presentedIndex - n; var popIndex = this.state.presentedIndex - n;
this._enableScene(popIndex); this._enableScene(popIndex);
this._emitWillFocus(this.state.routeStack[popIndex]);
this._transitionTo( this._transitionTo(
popIndex, popIndex,
null, // default velocity null, // default velocity
@ -954,16 +939,15 @@ var Navigator = React.createClass({
nextRouteStack[index] = route; nextRouteStack[index] = route;
nextAnimationModeStack[index] = this.props.configureScene(route); nextAnimationModeStack[index] = this.props.configureScene(route);
if (index === this.state.presentedIndex) {
this._emitWillFocus(route);
}
this.setState({ this.setState({
idStack: nextIDStack, idStack: nextIDStack,
routeStack: nextRouteStack, routeStack: nextRouteStack,
sceneConfigStack: nextAnimationModeStack, sceneConfigStack: nextAnimationModeStack,
updatingRangeStart: index,
updatingRangeLength: 1,
}, () => { }, () => {
this._resetUpdatingRange();
if (index === this.state.presentedIndex) { if (index === this.state.presentedIndex) {
this._emitWillFocus(route);
this._emitDidFocus(route); this._emitDidFocus(route);
} }
cb && cb(); cb && cb();
@ -1034,69 +1018,17 @@ var Navigator = React.createClass({
var newStackLength = index + 1; var newStackLength = index + 1;
// Remove any unneeded rendered routes. // Remove any unneeded rendered routes.
if (newStackLength < this.state.routeStack.length) { if (newStackLength < this.state.routeStack.length) {
var updatingRangeStart = newStackLength; // One past the top
var updatingRangeLength = this.state.routeStack.length - newStackLength + 1;
this.state.idStack.slice(newStackLength).map((removingId) => { this.state.idStack.slice(newStackLength).map((removingId) => {
this._itemRefs[removingId] = null; this._itemRefs[removingId] = null;
}); });
this.setState({ this.setState({
updatingRangeStart: updatingRangeStart,
updatingRangeLength: updatingRangeLength,
sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength), sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength),
idStack: this.state.idStack.slice(0, newStackLength), idStack: this.state.idStack.slice(0, newStackLength),
routeStack: this.state.routeStack.slice(0, newStackLength), routeStack: this.state.routeStack.slice(0, newStackLength),
}, this._resetUpdatingRange); });
} }
}, },
_renderOptimizedScenes: function() {
// To avoid rendering scenes that are not visible, we use
// updatingRangeStart and updatingRangeLength to track the scenes that need
// to be updated.
// To avoid visual glitches, we never re-render scenes during a transition.
// We assume that `state.updatingRangeLength` will have a length during the
// initial render of any scene
var shouldRenderScenes = this.state.updatingRangeLength !== 0;
if (shouldRenderScenes) {
return (
<StaticContainer shouldUpdate={true}>
<View
style={styles.transitioner}
{...this.panGesture.panHandlers}
onTouchStart={this._handleTouchStart}
onResponderTerminationRequest={
this._handleResponderTerminationRequest
}>
{this.state.routeStack.map(this._renderOptimizedScene)}
</View>
</StaticContainer>
);
}
// If no scenes are changing, we can save render time. React will notice
// that we are rendering a StaticContainer in the same place, so the
// existing element will be updated. When React asks the element
// shouldComponentUpdate, the StaticContainer will return false, and the
// children from the previous reconciliation will remain.
return (
<StaticContainer shouldUpdate={false} />
);
},
_renderOptimizedScene: function(route, i) {
var shouldRenderScene =
i >= this.state.updatingRangeStart &&
i <= this.state.updatingRangeStart + this.state.updatingRangeLength;
var scene = shouldRenderScene ? this._renderScene(route, i) : null;
return (
<StaticContainer
key={'nav' + i}
shouldUpdate={shouldRenderScene}>
{scene}
</StaticContainer>
);
},
_renderScene: function(route, i) { _renderScene: function(route, i) {
var child = this.props.renderScene( var child = this.props.renderScene(
route, route,
@ -1146,9 +1078,30 @@ var Navigator = React.createClass({
}, },
render: function() { render: function() {
var newRenderedSceneMap = new Map();
var scenes = this.state.routeStack.map((route, index) => {
var renderedScene;
if (this._renderedSceneMap.has(route) &&
index !== this.state.presentedIndex) {
renderedScene = this._renderedSceneMap.get(route);
} else {
renderedScene = this._renderScene(route, index);
}
newRenderedSceneMap.set(route, renderedScene);
return renderedScene;
});
this._renderedSceneMap = newRenderedSceneMap;
return ( return (
<View style={[styles.container, this.props.style]}> <View style={[styles.container, this.props.style]}>
{this._renderOptimizedScenes()} <View
style={styles.transitioner}
{...this.panGesture.panHandlers}
onTouchStart={this._handleTouchStart}
onResponderTerminationRequest={
this._handleResponderTerminationRequest
}>
{scenes}
</View>
{this._renderNavigationBar()} {this._renderNavigationBar()}
</View> </View>
); );

View File

@ -76,15 +76,17 @@ class MessageQueue {
* Public APIs * Public APIs
*/ */
processBatch(batch) { processBatch(batch) {
ReactUpdates.batchedUpdates(() => { guard(() => {
batch.forEach((call) => { ReactUpdates.batchedUpdates(() => {
let method = call.method === 'callFunctionReturnFlushedQueue' ? batch.forEach((call) => {
'__callFunction' : '__invokeCallback'; let method = call.method === 'callFunctionReturnFlushedQueue' ?
guard(() => this[method].apply(this, call.args)); '__callFunction' : '__invokeCallback';
guard(() => this[method].apply(this, call.args));
});
BridgeProfiling.profile('ReactUpdates.batchedUpdates()');
}); });
BridgeProfiling.profile('ReactUpdates.batchedUpdates()'); BridgeProfiling.profileEnd();
}); });
BridgeProfiling.profileEnd();
return this.flushedQueue(); return this.flushedQueue();
} }

View File

@ -997,6 +997,7 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
@"easeIn": @(RCTAnimationTypeEaseIn), @"easeIn": @(RCTAnimationTypeEaseIn),
@"easeOut": @(RCTAnimationTypeEaseOut), @"easeOut": @(RCTAnimationTypeEaseOut),
@"easeInEaseOut": @(RCTAnimationTypeEaseInEaseOut), @"easeInEaseOut": @(RCTAnimationTypeEaseInEaseOut),
@"keyboard": @(RCTAnimationTypeKeyboard),
}), RCTAnimationTypeEaseInEaseOut, integerValue) }), RCTAnimationTypeEaseInEaseOut, integerValue)
@end @end

View File

@ -0,0 +1,19 @@
/**
* 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.
*/
#import <MapKit/MapKit.h>
@interface RCTPointAnnotation : MKPointAnnotation <MKAnnotation>
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, assign) BOOL hasLeftCallout;
@property (nonatomic, assign) BOOL hasRightCallout;
@property (nonatomic, assign) BOOL animateDrop;
@end

View File

@ -0,0 +1,14 @@
/**
* 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.
*/
#import "RCTPointAnnotation.h"
@implementation RCTPointAnnotation
@end

View File

@ -58,20 +58,23 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
@implementation RCTAnimation @implementation RCTAnimation
static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType type) static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnimationType type)
{ {
switch (type) { switch (type) {
case RCTAnimationTypeLinear: case RCTAnimationTypeLinear:
return UIViewAnimationCurveLinear; return UIViewAnimationOptionCurveLinear;
case RCTAnimationTypeEaseIn: case RCTAnimationTypeEaseIn:
return UIViewAnimationCurveEaseIn; return UIViewAnimationOptionCurveEaseIn;
case RCTAnimationTypeEaseOut: case RCTAnimationTypeEaseOut:
return UIViewAnimationCurveEaseOut; return UIViewAnimationOptionCurveEaseOut;
case RCTAnimationTypeEaseInEaseOut: case RCTAnimationTypeEaseInEaseOut:
return UIViewAnimationCurveEaseInOut; return UIViewAnimationOptionCurveEaseInOut;
case RCTAnimationTypeKeyboard:
// http://stackoverflow.com/questions/18870447/how-to-use-the-default-ios7-uianimation-curve
return (UIViewAnimationOptions)(7 << 16);
default: default:
RCTLogError(@"Unsupported animation type %zd", type); RCTLogError(@"Unsupported animation type %zd", type);
return UIViewAnimationCurveEaseInOut; return UIViewAnimationOptionCurveEaseInOut;
} }
} }
@ -123,7 +126,7 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
} else { } else {
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState |
UIViewAnimationCurveFromRCTAnimationType(_animationType); UIViewAnimationOptionsFromRCTAnimationType(_animationType);
[UIView animateWithDuration:_duration [UIView animateWithDuration:_duration
delay:_delay delay:_delay

View File

@ -64,6 +64,7 @@
58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; }; 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; };
58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; }; 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; };
58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; }; 58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; };
63F014C01B02080B003B75D2 /* RCTPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F014BF1B02080B003B75D2 /* RCTPointAnnotation.m */; };
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; }; 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; }; 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; };
832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; }; 832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; };
@ -213,6 +214,8 @@
58114A4F1AAE93D500E7D092 /* RCTAsyncLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAsyncLocalStorage.h; sourceTree = "<group>"; }; 58114A4F1AAE93D500E7D092 /* RCTAsyncLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAsyncLocalStorage.h; sourceTree = "<group>"; };
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePickerManager.m; sourceTree = "<group>"; }; 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePickerManager.m; sourceTree = "<group>"; };
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = "<group>"; }; 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = "<group>"; };
63F014BE1B02080B003B75D2 /* RCTPointAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointAnnotation.h; sourceTree = "<group>"; };
63F014BF1B02080B003B75D2 /* RCTPointAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPointAnnotation.m; sourceTree = "<group>"; };
830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = "<group>"; }; 830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = "<group>"; };
830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = "<group>"; }; 830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = "<group>"; };
830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = "<group>"; }; 830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = "<group>"; };
@ -291,6 +294,8 @@
13B07FEE1A69327A00A75B9A /* RCTTiming.m */, 13B07FEE1A69327A00A75B9A /* RCTTiming.m */,
13E067481A70F434002CDEE1 /* RCTUIManager.h */, 13E067481A70F434002CDEE1 /* RCTUIManager.h */,
13E067491A70F434002CDEE1 /* RCTUIManager.m */, 13E067491A70F434002CDEE1 /* RCTUIManager.m */,
63F014BE1B02080B003B75D2 /* RCTPointAnnotation.h */,
63F014BF1B02080B003B75D2 /* RCTPointAnnotation.m */,
); );
path = Modules; path = Modules;
sourceTree = "<group>"; sourceTree = "<group>";
@ -599,6 +604,7 @@
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */, 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */,
137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */, 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */,
00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */, 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */,
63F014C01B02080B003B75D2 /* RCTPointAnnotation.m in Sources */,
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */, 14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */,
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */, 134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */, 13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,

View File

@ -15,4 +15,5 @@ typedef NS_ENUM(NSInteger, RCTAnimationType) {
RCTAnimationTypeEaseIn, RCTAnimationTypeEaseIn,
RCTAnimationTypeEaseOut, RCTAnimationTypeEaseOut,
RCTAnimationTypeEaseInEaseOut, RCTAnimationTypeEaseInEaseOut,
RCTAnimationTypeKeyboard,
}; };

View File

@ -1,10 +1,11 @@
// /**
// RCTConvert+CoreLocation.h * Copyright (c) 2015-present, Facebook, Inc.
// React * All rights reserved.
// *
// Created by Nick Lockwood on 12/04/2015. * This source code is licensed under the BSD-style license found in the
// Copyright (c) 2015 Facebook. All rights reserved. * 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.
*/
#import <CoreLocation/CoreLocation.h> #import <CoreLocation/CoreLocation.h>

View File

@ -1,10 +1,11 @@
// /**
// RCTConvert+CoreLocation.m * Copyright (c) 2015-present, Facebook, Inc.
// React * All rights reserved.
// *
// Created by Nick Lockwood on 12/04/2015. * This source code is licensed under the BSD-style license found in the
// Copyright (c) 2015 Facebook. All rights reserved. * 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.
*/
#import "RCTConvert+CoreLocation.h" #import "RCTConvert+CoreLocation.h"

View File

@ -1,13 +1,15 @@
// /**
// RCTConvert+MapKit.h * Copyright (c) 2015-present, Facebook, Inc.
// React * All rights reserved.
// *
// Created by Nick Lockwood on 12/04/2015. * This source code is licensed under the BSD-style license found in the
// Copyright (c) 2015 Facebook. All rights reserved. * 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.
*/
#import <MapKit/MapKit.h> #import <MapKit/MapKit.h>
#import "RCTPointAnnotation.h"
#import "RCTConvert.h" #import "RCTConvert.h"
@interface RCTConvert (MapKit) @interface RCTConvert (MapKit)
@ -16,8 +18,12 @@
+ (MKCoordinateRegion)MKCoordinateRegion:(id)json; + (MKCoordinateRegion)MKCoordinateRegion:(id)json;
+ (MKShape *)MKShape:(id)json; + (MKShape *)MKShape:(id)json;
+ (MKMapType)MKMapType:(id)json; + (MKMapType)MKMapType:(id)json;
+ (RCTPointAnnotation *)RCTPointAnnotation:(id)json;
typedef NSArray MKShapeArray; typedef NSArray MKShapeArray;
+ (MKShapeArray *)MKShapeArray:(id)json; + (MKShapeArray *)MKShapeArray:(id)json;
typedef NSArray RCTPointAnnotationArray;
+ (RCTPointAnnotationArray *)RCTPointAnnotationArray:(id)json;
@end @end

View File

@ -1,14 +1,15 @@
// /**
// RCTConvert+MapKit.m * Copyright (c) 2015-present, Facebook, Inc.
// React * All rights reserved.
// *
// Created by Nick Lockwood on 12/04/2015. * This source code is licensed under the BSD-style license found in the
// Copyright (c) 2015 Facebook. All rights reserved. * 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.
*/
#import "RCTConvert+MapKit.h" #import "RCTConvert+MapKit.h"
#import "RCTConvert+CoreLocation.h" #import "RCTConvert+CoreLocation.h"
#import "RCTPointAnnotation.h"
@implementation RCTConvert(MapKit) @implementation RCTConvert(MapKit)
@ -49,4 +50,20 @@ RCT_ENUM_CONVERTER(MKMapType, (@{
@"hybrid": @(MKMapTypeHybrid), @"hybrid": @(MKMapTypeHybrid),
}), MKMapTypeStandard, integerValue) }), MKMapTypeStandard, integerValue)
+ (RCTPointAnnotation *)RCTPointAnnotation:(id)json
{
json = [self NSDictionary:json];
RCTPointAnnotation *shape = [[RCTPointAnnotation alloc] init];
shape.coordinate = [self CLLocationCoordinate2D: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"]];
return shape;
}
RCT_ARRAY_CONVERTER(RCTPointAnnotation)
@end @end

View File

@ -26,7 +26,8 @@ extern const CGFloat RCTMapZoomBoundBuffer;
@property (nonatomic, assign) CGFloat maxDelta; @property (nonatomic, assign) CGFloat maxDelta;
@property (nonatomic, assign) UIEdgeInsets legalLabelInsets; @property (nonatomic, assign) UIEdgeInsets legalLabelInsets;
@property (nonatomic, strong) NSTimer *regionChangeObserveTimer; @property (nonatomic, strong) NSTimer *regionChangeObserveTimer;
@property (nonatomic, strong) NSMutableArray *annotationIds;
- (void)setAnnotations:(MKShapeArray *)annotations; - (void)setAnnotations:(RCTPointAnnotationArray *)annotations;
@end @end

View File

@ -112,12 +112,52 @@ const CGFloat RCTMapZoomBoundBuffer = 0.01;
[super setRegion:region animated:animated]; [super setRegion:region animated:animated];
} }
- (void)setAnnotations:(MKShapeArray *)annotations - (void)setAnnotations:(RCTPointAnnotationArray *)annotations
{ {
[self removeAnnotations:self.annotations]; NSMutableArray *newAnnotationIds = [[NSMutableArray alloc] init];
if (annotations.count) { NSMutableArray *annotationsToDelete = [[NSMutableArray alloc] init];
[self addAnnotations:annotations]; NSMutableArray *annotationsToAdd = [[NSMutableArray alloc] init];
for (RCTPointAnnotation *annotation in annotations) {
if (![annotation isKindOfClass:[RCTPointAnnotation class]]) {
continue;
}
[newAnnotationIds addObject:annotation.identifier];
// If the current set does not contain the new annotation, mark it as add
if (![self.annotationIds containsObject:annotation.identifier]) {
[annotationsToAdd addObject:annotation];
}
} }
for (RCTPointAnnotation *annotation in self.annotations) {
if (![annotation isKindOfClass:[RCTPointAnnotation class]]) {
continue;
}
// If the new set does not contain an existing annotation, mark it as delete
if (![newAnnotationIds containsObject:annotation.identifier]) {
[annotationsToDelete addObject:annotation];
}
}
if (annotationsToDelete.count) {
[self removeAnnotations:annotationsToDelete];
}
if (annotationsToAdd.count) {
[self addAnnotations:annotationsToAdd];
}
NSMutableArray *newIds = [[NSMutableArray alloc] init];
for (RCTPointAnnotation *anno in self.annotations) {
if ([anno isKindOfClass:[MKUserLocation class]]) {
continue;
}
[newIds addObject:anno.identifier];
}
self.annotationIds = newIds;
} }
@end @end

View File

@ -15,6 +15,9 @@
#import "RCTEventDispatcher.h" #import "RCTEventDispatcher.h"
#import "RCTMap.h" #import "RCTMap.h"
#import "UIView+React.h" #import "UIView+React.h"
#import "RCTPointAnnotation.h"
#import <MapKit/MapKit.h>
static NSString *const RCTMapViewKey = @"MapView"; static NSString *const RCTMapViewKey = @"MapView";
@ -42,7 +45,7 @@ RCT_EXPORT_VIEW_PROPERTY(maxDelta, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat) RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets) RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType) RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType)
RCT_EXPORT_VIEW_PROPERTY(annotations, MKShapeArray) RCT_EXPORT_VIEW_PROPERTY(annotations, RCTPointAnnotationArray)
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
{ {
[view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES]; [view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES];
@ -50,6 +53,73 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
#pragma mark MKMapViewDelegate #pragma mark MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if (![view.annotation isKindOfClass:[MKUserLocation class]]) {
RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation;
NSString *title = view.annotation.title ?: @"";
NSString *subtitle = view.annotation.subtitle ?: @"";
NSDictionary *event = @{
@"target": mapView.reactTag,
@"action": @"annotation-click",
@"annotation": @{
@"id": annotation.identifier,
@"title": title,
@"subtitle": subtitle,
@"latitude": @(annotation.coordinate.latitude),
@"longitude": @(annotation.coordinate.longitude)
}
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topTap" body:event];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(RCTPointAnnotation *)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
MKPinAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"RCTAnnotation"];
annotationView.canShowCallout = true;
annotationView.animatesDrop = annotation.animateDrop;
annotationView.leftCalloutAccessoryView = nil;
if (annotation.hasLeftCallout) {
annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
annotationView.rightCalloutAccessoryView = nil;
if (annotation.hasRightCallout) {
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
return annotationView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// Pass to js
RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation;
NSString *side = (control == view.leftCalloutAccessoryView) ? @"left" : @"right";
NSDictionary *event = @{
@"target": mapView.reactTag,
@"side": side,
@"action": @"callout-click",
@"annotationId": annotation.identifier
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topTap" body:event];
}
- (void)mapView:(RCTMap *)mapView didUpdateUserLocation:(MKUserLocation *)location - (void)mapView:(RCTMap *)mapView didUpdateUserLocation:(MKUserLocation *)location
{ {
if (mapView.followUserLocation) { if (mapView.followUserLocation) {
@ -143,7 +213,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
#define FLUSH_NAN(value) (isnan(value) ? 0 : value) #define FLUSH_NAN(value) (isnan(value) ? 0 : value)
NSDictionary *event = @{ NSDictionary *event = @{
@"target": [mapView reactTag], @"target": mapView.reactTag,
@"continuous": @(continuous), @"continuous": @(continuous),
@"region": @{ @"region": @{
@"latitude": @(FLUSH_NAN(region.center.latitude)), @"latitude": @(FLUSH_NAN(region.center.latitude)),

View File

@ -22,6 +22,7 @@
@property (nonatomic, strong) UIColor *tintColor; @property (nonatomic, strong) UIColor *tintColor;
@property (nonatomic, strong) UIColor *barTintColor; @property (nonatomic, strong) UIColor *barTintColor;
@property (nonatomic, strong) UIColor *titleTextColor; @property (nonatomic, strong) UIColor *titleTextColor;
@property (nonatomic, assign) BOOL translucent;
@property (nonatomic, readonly) UIBarButtonItem *backButtonItem; @property (nonatomic, readonly) UIBarButtonItem *backButtonItem;
@property (nonatomic, readonly) UIBarButtonItem *leftButtonItem; @property (nonatomic, readonly) UIBarButtonItem *leftButtonItem;

View File

@ -24,6 +24,7 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL) RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(title, NSString) RCT_EXPORT_VIEW_PROPERTY(title, NSString)
RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor)

View File

@ -1,10 +1,11 @@
// /**
// RCTSegmentedControl.h * Copyright (c) 2015-present, Facebook, Inc.
// React * All rights reserved.
// *
// Created by Clay Allsopp on 3/31/15. * This source code is licensed under the BSD-style license found in the
// Copyright (c) 2015 Facebook. All rights reserved. * 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.
*/
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>

View File

@ -1,10 +1,11 @@
// /**
// RCTSegmentedControl.m * Copyright (c) 2015-present, Facebook, Inc.
// React * All rights reserved.
// *
// Created by Clay Allsopp on 3/31/15. * This source code is licensed under the BSD-style license found in the
// Copyright (c) 2015 Facebook. All rights reserved. * 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.
*/
#import "RCTSegmentedControl.h" #import "RCTSegmentedControl.h"

View File

@ -81,6 +81,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
UINavigationBar *bar = self.navigationController.navigationBar; UINavigationBar *bar = self.navigationController.navigationBar;
bar.barTintColor = _navItem.barTintColor; bar.barTintColor = _navItem.barTintColor;
bar.tintColor = _navItem.tintColor; bar.tintColor = _navItem.tintColor;
bar.translucent = _navItem.translucent;
if (_navItem.titleTextColor) { if (_navItem.titleTextColor) {
[bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}]; [bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}];
} }