mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-29 12:45:37 +08:00
MapView to support MKPointAnnotation using new attribute annotate in Map...
Summary:
### MapView to support Pin annotation
var pinLocation = {
latitude: property.latitude,
longitude: property.longitude,
title: property.title
};
this.state = {propertyPoint: pinLocation};
<MapView style={styles.map} region={this.state.region} annotate={this.state.propertyPoint}>
</MapView>

Closes https://github.com/facebook/react-native/pull/810
Github Author: guru inamdar <guru.inamdar@gmail.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
This commit is contained in:
@@ -24,33 +24,44 @@ var {
|
|||||||
View,
|
View,
|
||||||
} = React;
|
} = React;
|
||||||
|
|
||||||
|
var regionText = {
|
||||||
|
latitude: '0',
|
||||||
|
longitude: '0',
|
||||||
|
latitudeDelta: '0',
|
||||||
|
longitudeDelta: '0',
|
||||||
|
}
|
||||||
|
|
||||||
var MapRegionInput = React.createClass({
|
var MapRegionInput = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
region: React.PropTypes.shape({
|
region: React.PropTypes.shape({
|
||||||
latitude: React.PropTypes.number,
|
latitude: React.PropTypes.number.isRequired,
|
||||||
longitude: React.PropTypes.number,
|
longitude: React.PropTypes.number.isRequired,
|
||||||
latitudeDelta: React.PropTypes.number,
|
latitudeDelta: React.PropTypes.number.isRequired,
|
||||||
longitudeDelta: React.PropTypes.number,
|
longitudeDelta: React.PropTypes.number.isRequired,
|
||||||
}),
|
}),
|
||||||
onChange: React.PropTypes.func.isRequired,
|
onChange: React.PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
latitude: 0,
|
region: {
|
||||||
longitude: 0,
|
latitude: 0,
|
||||||
latitudeDelta: 0,
|
longitude: 0,
|
||||||
longitudeDelta: 0,
|
latitudeDelta: 0,
|
||||||
|
longitudeDelta: 0,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillReceiveProps: function(nextProps) {
|
componentWillReceiveProps: function(nextProps) {
|
||||||
this.setState(nextProps.region);
|
this.setState({
|
||||||
|
region: nextProps.region || this.getInitialState().region
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var region = this.state;
|
var region = this.state.region || this.getInitialState().region;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
@@ -61,6 +72,7 @@ var MapRegionInput = React.createClass({
|
|||||||
value={'' + region.latitude}
|
value={'' + region.latitude}
|
||||||
style={styles.textInput}
|
style={styles.textInput}
|
||||||
onChange={this._onChangeLatitude}
|
onChange={this._onChangeLatitude}
|
||||||
|
selectTextOnFocus={true}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
@@ -71,6 +83,7 @@ var MapRegionInput = React.createClass({
|
|||||||
value={'' + region.longitude}
|
value={'' + region.longitude}
|
||||||
style={styles.textInput}
|
style={styles.textInput}
|
||||||
onChange={this._onChangeLongitude}
|
onChange={this._onChangeLongitude}
|
||||||
|
selectTextOnFocus={true}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
@@ -81,6 +94,7 @@ var MapRegionInput = React.createClass({
|
|||||||
value={'' + region.latitudeDelta}
|
value={'' + region.latitudeDelta}
|
||||||
style={styles.textInput}
|
style={styles.textInput}
|
||||||
onChange={this._onChangeLatitudeDelta}
|
onChange={this._onChangeLatitudeDelta}
|
||||||
|
selectTextOnFocus={true}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
@@ -91,6 +105,7 @@ var MapRegionInput = React.createClass({
|
|||||||
value={'' + region.longitudeDelta}
|
value={'' + region.longitudeDelta}
|
||||||
style={styles.textInput}
|
style={styles.textInput}
|
||||||
onChange={this._onChangeLongitudeDelta}
|
onChange={this._onChangeLongitudeDelta}
|
||||||
|
selectTextOnFocus={true}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.changeButton}>
|
<View style={styles.changeButton}>
|
||||||
@@ -103,23 +118,29 @@ var MapRegionInput = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onChangeLatitude: function(e) {
|
_onChangeLatitude: function(e) {
|
||||||
this.setState({latitude: parseFloat(e.nativeEvent.text)});
|
regionText.latitude = e.nativeEvent.text;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onChangeLongitude: function(e) {
|
_onChangeLongitude: function(e) {
|
||||||
this.setState({longitude: parseFloat(e.nativeEvent.text)});
|
regionText.longitude = e.nativeEvent.text;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onChangeLatitudeDelta: function(e) {
|
_onChangeLatitudeDelta: function(e) {
|
||||||
this.setState({latitudeDelta: parseFloat(e.nativeEvent.text)});
|
regionText.latitudeDelta = e.nativeEvent.text;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onChangeLongitudeDelta: function(e) {
|
_onChangeLongitudeDelta: function(e) {
|
||||||
this.setState({longitudeDelta: parseFloat(e.nativeEvent.text)});
|
regionText.longitudeDelta = e.nativeEvent.text;
|
||||||
},
|
},
|
||||||
|
|
||||||
_change: function() {
|
_change: function() {
|
||||||
this.props.onChange(this.state);
|
this.setState({
|
||||||
|
latitude: parseFloat(regionText.latitude),
|
||||||
|
longitude: parseFloat(regionText.longitude),
|
||||||
|
latitudeDelta: parseFloat(regionText.latitudeDelta),
|
||||||
|
longitudeDelta: parseFloat(regionText.longitudeDelta),
|
||||||
|
});
|
||||||
|
this.props.onChange(this.state.region);
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -130,6 +151,8 @@ var MapViewExample = React.createClass({
|
|||||||
return {
|
return {
|
||||||
mapRegion: null,
|
mapRegion: null,
|
||||||
mapRegionInput: null,
|
mapRegionInput: null,
|
||||||
|
annotations: null,
|
||||||
|
isFirstLoad: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -138,8 +161,10 @@ var MapViewExample = React.createClass({
|
|||||||
<View>
|
<View>
|
||||||
<MapView
|
<MapView
|
||||||
style={styles.map}
|
style={styles.map}
|
||||||
onRegionChange={this._onRegionChanged}
|
onRegionChange={this._onRegionChange}
|
||||||
|
onRegionChangeComplete={this._onRegionChangeComplete}
|
||||||
region={this.state.mapRegion}
|
region={this.state.mapRegion}
|
||||||
|
annotations={this.state.annotations}
|
||||||
/>
|
/>
|
||||||
<MapRegionInput
|
<MapRegionInput
|
||||||
onChange={this._onRegionInputChanged}
|
onChange={this._onRegionInputChanged}
|
||||||
@@ -149,14 +174,35 @@ var MapViewExample = React.createClass({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onRegionChanged(region) {
|
_getAnnotations(region) {
|
||||||
this.setState({mapRegionInput: region});
|
return [{
|
||||||
|
longitude: region.longitude,
|
||||||
|
latitude: region.latitude,
|
||||||
|
title: 'You Are Here',
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRegionChange(region) {
|
||||||
|
this.setState({
|
||||||
|
mapRegionInput: region,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRegionChangeComplete(region) {
|
||||||
|
if (this.state.isFirstLoad) {
|
||||||
|
this.setState({
|
||||||
|
mapRegionInput: region,
|
||||||
|
annotations: this._getAnnotations(region),
|
||||||
|
isFirstLoad: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onRegionInputChanged(region) {
|
_onRegionInputChanged(region) {
|
||||||
this.setState({
|
this.setState({
|
||||||
mapRegion: region,
|
mapRegion: region,
|
||||||
mapRegionInput: region,
|
mapRegionInput: region,
|
||||||
|
annotations: this._getAnnotations(region),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ var styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
width: 80,
|
width: 120,
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
@@ -311,4 +311,29 @@ exports.examples = [
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Clear and select',
|
||||||
|
render: function () {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<WithLabel label="clearTextOnFocus">
|
||||||
|
<TextInput
|
||||||
|
placeholder="text is cleared on focus"
|
||||||
|
value="text is cleared on focus"
|
||||||
|
style={styles.default}
|
||||||
|
clearTextOnFocus={true}
|
||||||
|
/>
|
||||||
|
</WithLabel>
|
||||||
|
<WithLabel label="selectTextOnFocus">
|
||||||
|
<TextInput
|
||||||
|
placeholder="text is selected on focus"
|
||||||
|
value="text is selected on focus"
|
||||||
|
style={styles.default}
|
||||||
|
selectTextOnFocus={true}
|
||||||
|
/>
|
||||||
|
</WithLabel>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
@@ -39,9 +39,10 @@
|
|||||||
#endif
|
#endif
|
||||||
NSString *version = [[UIDevice currentDevice] systemVersion];
|
NSString *version = [[UIDevice currentDevice] systemVersion];
|
||||||
RCTAssert([version isEqualToString:@"8.1"], @"Snapshot tests should be run on iOS 8.1, found %@", version);
|
RCTAssert([version isEqualToString:@"8.1"], @"Snapshot tests should be run on iOS 8.1, found %@", version);
|
||||||
_runner = initRunnerForApp(@"Examples/UIExplorer/UIExplorerApp");
|
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp");
|
||||||
|
|
||||||
// If tests have changes, set recordMode = YES below and run the affected tests on an iPhone5, iOS 8.1 simulator.
|
// If tests have changes, set recordMode = YES below and run the affected
|
||||||
|
// tests on an iPhone5, iOS 8.1 simulator.
|
||||||
_runner.recordMode = NO;
|
_runner.recordMode = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +59,10 @@
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure this test runs first (underscores sort early) otherwise the other tests will tear out the rootView
|
// Make sure this test runs first (underscores sort early) otherwise the
|
||||||
- (void)test__RootViewLoadsAndRenders {
|
// other tests will tear out the rootView
|
||||||
|
- (void)test__RootViewLoadsAndRenders
|
||||||
|
{
|
||||||
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
||||||
RCTAssert([vc.view isKindOfClass:[RCTRootView class]], @"This test must run first.");
|
RCTAssert([vc.view isKindOfClass:[RCTRootView class]], @"This test must run first.");
|
||||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation IntegrationTestsTests {
|
@implementation IntegrationTestsTests
|
||||||
|
{
|
||||||
RCTTestRunner *_runner;
|
RCTTestRunner *_runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,10 +29,11 @@
|
|||||||
RCTAssert(!__LP64__, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
|
RCTAssert(!__LP64__, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
|
||||||
#endif
|
#endif
|
||||||
NSString *version = [[UIDevice currentDevice] systemVersion];
|
NSString *version = [[UIDevice currentDevice] systemVersion];
|
||||||
RCTAssert([version isEqualToString:@"8.1"], @"Tests should be run on iOS 8.1, found %@", version);
|
RCTAssert([version integerValue] == 8, @"Tests should be run on iOS 8.x, found %@", version);
|
||||||
_runner = initRunnerForApp(@"IntegrationTests/IntegrationTestsApp");
|
_runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp");
|
||||||
|
|
||||||
// If tests have changes, set recordMode = YES below and run the affected tests on an iPhone5, iOS 8.1 simulator.
|
// If tests have changes, set recordMode = YES below and run the affected
|
||||||
|
// tests on an iPhone5, iOS 8.1 simulator.
|
||||||
_runner.recordMode = NO;
|
_runner.recordMode = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,15 +46,19 @@
|
|||||||
|
|
||||||
- (void)testTheTester_waitOneFrame
|
- (void)testTheTester_waitOneFrame
|
||||||
{
|
{
|
||||||
[_runner runTest:_cmd module:@"IntegrationTestHarnessTest" initialProps:@{@"waitOneFrame": @YES} expectErrorBlock:nil];
|
[_runner runTest:_cmd
|
||||||
|
module:@"IntegrationTestHarnessTest"
|
||||||
|
initialProps:@{@"waitOneFrame": @YES}
|
||||||
|
expectErrorBlock:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testTheTester_ExpectError
|
// TODO: this seems to stall forever - figure out why
|
||||||
|
- (void)DISABLED_testTheTester_ExpectError
|
||||||
{
|
{
|
||||||
[_runner runTest:_cmd
|
[_runner runTest:_cmd
|
||||||
module:@"IntegrationTestHarnessTest"
|
module:@"IntegrationTestHarnessTest"
|
||||||
initialProps:@{@"shouldThrow": @YES}
|
initialProps:@{@"shouldThrow": @YES}
|
||||||
expectErrorRegex:[NSRegularExpression regularExpressionWithPattern:@"because shouldThrow" options:0 error:nil]];
|
expectErrorRegex:@"because shouldThrow"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testTimers
|
- (void)testTimers
|
||||||
|
|||||||
@@ -95,6 +95,23 @@ var MapView = React.createClass({
|
|||||||
longitudeDelta: React.PropTypes.number.isRequired,
|
longitudeDelta: React.PropTypes.number.isRequired,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map annotations with title/subtitle.
|
||||||
|
*/
|
||||||
|
annotations: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||||
|
/**
|
||||||
|
* The location of the annotation.
|
||||||
|
*/
|
||||||
|
latitude: React.PropTypes.number.isRequired,
|
||||||
|
longitude: React.PropTypes.number.isRequired,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation title/subtile.
|
||||||
|
*/
|
||||||
|
title: React.PropTypes.string,
|
||||||
|
subtitle: React.PropTypes.string,
|
||||||
|
})),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum size of area that can be displayed.
|
* Maximum size of area that can be displayed.
|
||||||
*/
|
*/
|
||||||
@@ -142,6 +159,7 @@ var MapView = React.createClass({
|
|||||||
pitchEnabled={this.props.pitchEnabled}
|
pitchEnabled={this.props.pitchEnabled}
|
||||||
scrollEnabled={this.props.scrollEnabled}
|
scrollEnabled={this.props.scrollEnabled}
|
||||||
region={this.props.region}
|
region={this.props.region}
|
||||||
|
annotations={this.props.annotations}
|
||||||
maxDelta={this.props.maxDelta}
|
maxDelta={this.props.maxDelta}
|
||||||
minDelta={this.props.minDelta}
|
minDelta={this.props.minDelta}
|
||||||
legalLabelInsets={this.props.legalLabelInsets}
|
legalLabelInsets={this.props.legalLabelInsets}
|
||||||
@@ -165,6 +183,7 @@ var RCTMap = createReactIOSNativeComponentClass({
|
|||||||
pitchEnabled: true,
|
pitchEnabled: true,
|
||||||
scrollEnabled: true,
|
scrollEnabled: true,
|
||||||
region: {diff: deepDiffer},
|
region: {diff: deepDiffer},
|
||||||
|
annotations: {diff: deepDiffer},
|
||||||
maxDelta: true,
|
maxDelta: true,
|
||||||
minDelta: true,
|
minDelta: true,
|
||||||
legalLabelInsets: {diff: insetsDiffer},
|
legalLabelInsets: {diff: insetsDiffer},
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ var RCTTextFieldAttributes = merge(RCTTextViewAttributes, {
|
|||||||
caretHidden: true,
|
caretHidden: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
clearButtonMode: true,
|
clearButtonMode: true,
|
||||||
|
clearTextOnFocus: true,
|
||||||
|
selectTextOnFocus: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var onlyMultiline = {
|
var onlyMultiline = {
|
||||||
@@ -267,7 +269,17 @@ var TextInput = React.createClass({
|
|||||||
'unless-editing',
|
'unless-editing',
|
||||||
'always',
|
'always',
|
||||||
]),
|
]),
|
||||||
|
/**
|
||||||
|
* If true, clears the text field automatically when editing begins
|
||||||
|
*/
|
||||||
|
clearTextOnFocus: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* If true, selected the text automatically when editing begins
|
||||||
|
*/
|
||||||
|
selectTextOnFocus: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Styles
|
||||||
|
*/
|
||||||
style: Text.propTypes.style,
|
style: Text.propTypes.style,
|
||||||
/**
|
/**
|
||||||
* Used to locate this view in end-to-end tests.
|
* Used to locate this view in end-to-end tests.
|
||||||
@@ -431,6 +443,8 @@ var TextInput = React.createClass({
|
|||||||
autoCapitalize={autoCapitalize}
|
autoCapitalize={autoCapitalize}
|
||||||
autoCorrect={this.props.autoCorrect}
|
autoCorrect={this.props.autoCorrect}
|
||||||
clearButtonMode={clearButtonMode}
|
clearButtonMode={clearButtonMode}
|
||||||
|
clearTextOnFocus={this.props.clearTextOnFocus}
|
||||||
|
selectTextOnFocus={this.props.selectTextOnFocus}
|
||||||
/>;
|
/>;
|
||||||
} else {
|
} else {
|
||||||
for (var propKey in notMultiline) {
|
for (var propKey in notMultiline) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*
|
*
|
||||||
* @providesModule loadSourceMap
|
* @providesModule loadSourceMap
|
||||||
* @flow
|
* -- disabled flow due to mysterious validation errors --
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the initRunnerForApp macro for typical usage.
|
* Use the RCTInitRunnerForApp macro for typical usage.
|
||||||
*
|
*
|
||||||
* Add this to your test target's gcc preprocessor macros:
|
* Add this to your test target's gcc preprocessor macros:
|
||||||
*
|
*
|
||||||
* FB_REFERENCE_IMAGE_DIR="\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""
|
* FB_REFERENCE_IMAGE_DIR="\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""
|
||||||
*/
|
*/
|
||||||
#define initRunnerForApp(app__) [[RCTTestRunner alloc] initWithApp:(app__) referenceDir:@FB_REFERENCE_IMAGE_DIR]
|
#define RCTInitRunnerForApp(app__) [[RCTTestRunner alloc] initWithApp:(app__) referenceDir:@FB_REFERENCE_IMAGE_DIR]
|
||||||
|
|
||||||
@interface RCTTestRunner : NSObject
|
@interface RCTTestRunner : NSObject
|
||||||
|
|
||||||
@@ -24,22 +24,25 @@
|
|||||||
@property (nonatomic, strong) NSURL *scriptURL;
|
@property (nonatomic, strong) NSURL *scriptURL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a runner. It's recommended that you use the initRunnerForApp macro instead of calling this directly.
|
* Initialize a runner. It's recommended that you use the RCTInitRunnerForApp
|
||||||
|
* macro instead of calling this directly.
|
||||||
*
|
*
|
||||||
* @param app The path to the app bundle without suffixes, e.g. IntegrationTests/IntegrationTestsApp
|
* @param app The path to the app bundle without suffixes, e.g. IntegrationTests/IntegrationTestsApp
|
||||||
* @param referencesDir The path for snapshot references images. The initRunnerForApp macro uses
|
* @param referencesDir The path for snapshot references images. The RCTInitRunnerForApp macro uses
|
||||||
* FB_REFERENCE_IMAGE_DIR for this automatically.
|
* FB_REFERENCE_IMAGE_DIR for this automatically.
|
||||||
*/
|
*/
|
||||||
- (instancetype)initWithApp:(NSString *)app referenceDir:(NSString *)referenceDir;
|
- (instancetype)initWithApp:(NSString *)app referenceDir:(NSString *)referenceDir;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplest runTest function simply mounts the specified JS module with no initialProps and waits for it to call
|
* Simplest runTest function simply mounts the specified JS module with no
|
||||||
|
* initialProps and waits for it to call
|
||||||
*
|
*
|
||||||
* RCTTestModule.markTestCompleted()
|
* RCTTestModule.markTestCompleted()
|
||||||
*
|
*
|
||||||
* JS errors/exceptions and timeouts will fail the test. Snapshot tests call RCTTestModule.verifySnapshot whenever they
|
* JS errors/exceptions and timeouts will fail the test. Snapshot tests call
|
||||||
* want to verify what has been rendered (typically via requestAnimationFrame to make sure the latest state has been
|
* RCTTestModule.verifySnapshot whenever they want to verify what has been
|
||||||
* rendered in native.
|
* rendered (typically via requestAnimationFrame to make sure the latest state
|
||||||
|
* has been rendered in native.
|
||||||
*
|
*
|
||||||
* @param test Selector of the test, usually just `_cmd`.
|
* @param test Selector of the test, usually just `_cmd`.
|
||||||
* @param moduleName Name of the JS component as registered by `AppRegistry.registerComponent` in JS.
|
* @param moduleName Name of the JS component as registered by `AppRegistry.registerComponent` in JS.
|
||||||
@@ -47,8 +50,9 @@
|
|||||||
- (void)runTest:(SEL)test module:(NSString *)moduleName;
|
- (void)runTest:(SEL)test module:(NSString *)moduleName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as runTest:, but allows for passing initialProps for providing mock data or requesting different behaviors, and
|
* Same as runTest:, but allows for passing initialProps for providing mock data
|
||||||
* expectErrorRegex verifies that the error you expected was thrown.
|
* or requesting different behaviors, and expectErrorRegex verifies that the
|
||||||
|
* error you expected was thrown.
|
||||||
*
|
*
|
||||||
* @param test Selector of the test, usually just `_cmd`.
|
* @param test Selector of the test, usually just `_cmd`.
|
||||||
* @param moduleName Name of the JS component as registered by `AppRegistry.registerComponent` in JS.
|
* @param moduleName Name of the JS component as registered by `AppRegistry.registerComponent` in JS.
|
||||||
@@ -58,8 +62,9 @@
|
|||||||
- (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorRegex:(NSString *)expectErrorRegex;
|
- (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorRegex:(NSString *)expectErrorRegex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as runTest:, but allows for passing initialProps for providing mock data or requesting different behaviors, and
|
* Same as runTest:, but allows for passing initialProps for providing mock data
|
||||||
* expectErrorBlock provides arbitrary logic for processing errors (nil will cause any error to fail the test).
|
* or requesting different behaviors, and expectErrorBlock provides arbitrary
|
||||||
|
* logic for processing errors (nil will cause any error to fail the test).
|
||||||
*
|
*
|
||||||
* @param test Selector of the test, usually just `_cmd`.
|
* @param test Selector of the test, usually just `_cmd`.
|
||||||
* @param moduleName Name of the JS component as registered by `AppRegistry.registerComponent` in JS.
|
* @param moduleName Name of the JS component as registered by `AppRegistry.registerComponent` in JS.
|
||||||
|
|||||||
@@ -49,7 +49,8 @@
|
|||||||
[self runTest:test module:moduleName initialProps:nil expectErrorBlock:nil];
|
[self runTest:test module:moduleName initialProps:nil expectErrorBlock:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)runTest:(SEL)test module:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorRegex:(NSString *)errorRegex
|
- (void)runTest:(SEL)test module:(NSString *)moduleName
|
||||||
|
initialProps:(NSDictionary *)initialProps expectErrorRegex:(NSString *)errorRegex
|
||||||
{
|
{
|
||||||
[self runTest:test module:moduleName initialProps:initialProps expectErrorBlock:^BOOL(NSString *error){
|
[self runTest:test module:moduleName initialProps:initialProps expectErrorBlock:^BOOL(NSString *error){
|
||||||
return [error rangeOfString:errorRegex options:NSRegularExpressionSearch].location != NSNotFound;
|
return [error rangeOfString:errorRegex options:NSRegularExpressionSearch].location != NSNotFound;
|
||||||
|
|||||||
@@ -114,7 +114,9 @@
|
|||||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||||||
{
|
{
|
||||||
CGFloat fraction;
|
CGFloat fraction;
|
||||||
NSUInteger characterIndex = [_layoutManager characterIndexForPoint:point inTextContainer:_textContainer fractionOfDistanceBetweenInsertionPoints:&fraction];
|
NSUInteger characterIndex = [_layoutManager characterIndexForPoint:point
|
||||||
|
inTextContainer:_textContainer
|
||||||
|
fractionOfDistanceBetweenInsertionPoints:&fraction];
|
||||||
|
|
||||||
NSNumber *reactTag = nil;
|
NSNumber *reactTag = nil;
|
||||||
|
|
||||||
|
|||||||
@@ -106,11 +106,14 @@
|
|||||||
{
|
{
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
[_touchHandler invalidate];
|
[_touchHandler invalidate];
|
||||||
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
if (_contentView) {
|
||||||
args:@[_contentView.reactTag]];
|
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
||||||
|
args:@[_contentView.reactTag]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIViewController *)backingViewController {
|
- (UIViewController *)backingViewController
|
||||||
|
{
|
||||||
return _backingViewController ?: [super backingViewController];
|
return _backingViewController ?: [super backingViewController];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; };
|
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; };
|
||||||
00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */; };
|
00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */; };
|
||||||
|
13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; };
|
||||||
|
13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; };
|
||||||
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; };
|
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; };
|
||||||
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
|
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
|
||||||
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
|
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
|
||||||
@@ -83,6 +85,10 @@
|
|||||||
13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = "<group>"; };
|
13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = "<group>"; };
|
||||||
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; };
|
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; };
|
||||||
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = "<group>"; };
|
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = "<group>"; };
|
||||||
|
13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+CoreLocation.h"; sourceTree = "<group>"; };
|
||||||
|
13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CoreLocation.m"; sourceTree = "<group>"; };
|
||||||
|
13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+MapKit.h"; sourceTree = "<group>"; };
|
||||||
|
13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+MapKit.m"; sourceTree = "<group>"; };
|
||||||
134FCB391A6E7F0800051CC8 /* RCTContextExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTContextExecutor.h; sourceTree = "<group>"; };
|
134FCB391A6E7F0800051CC8 /* RCTContextExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTContextExecutor.h; sourceTree = "<group>"; };
|
||||||
134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutor.m; sourceTree = "<group>"; };
|
134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutor.m; sourceTree = "<group>"; };
|
||||||
134FCB3B1A6E7F0800051CC8 /* RCTWebViewExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewExecutor.h; sourceTree = "<group>"; };
|
134FCB3B1A6E7F0800051CC8 /* RCTWebViewExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewExecutor.h; sourceTree = "<group>"; };
|
||||||
@@ -255,6 +261,10 @@
|
|||||||
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */,
|
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */,
|
||||||
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */,
|
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */,
|
||||||
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */,
|
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */,
|
||||||
|
13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */,
|
||||||
|
13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */,
|
||||||
|
13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */,
|
||||||
|
13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */,
|
||||||
14435CE11AAC4AE100FC20F4 /* RCTMap.h */,
|
14435CE11AAC4AE100FC20F4 /* RCTMap.h */,
|
||||||
14435CE21AAC4AE100FC20F4 /* RCTMap.m */,
|
14435CE21AAC4AE100FC20F4 /* RCTMap.m */,
|
||||||
14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */,
|
14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */,
|
||||||
@@ -459,6 +469,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */,
|
||||||
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */,
|
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */,
|
||||||
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */,
|
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */,
|
||||||
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
|
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
|
||||||
@@ -490,6 +501,7 @@
|
|||||||
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */,
|
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */,
|
||||||
13B0801D1A69489C00A75B9A /* RCTNavItemManager.m in Sources */,
|
13B0801D1A69489C00A75B9A /* RCTNavItemManager.m in Sources */,
|
||||||
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */,
|
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */,
|
||||||
|
13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */,
|
||||||
137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */,
|
137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */,
|
||||||
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */,
|
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */,
|
||||||
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
|
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
|
||||||
|
|||||||
19
React/Views/RCTConvert+CoreLocation.h
Normal file
19
React/Views/RCTConvert+CoreLocation.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// RCTConvert+CoreLocation.h
|
||||||
|
// React
|
||||||
|
//
|
||||||
|
// Created by Nick Lockwood on 12/04/2015.
|
||||||
|
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <CoreLocation/CoreLocation.h>
|
||||||
|
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
|
||||||
|
@interface RCTConvert (CoreLocation)
|
||||||
|
|
||||||
|
+ (CLLocationDegrees)CLLocationDegrees:(id)json;
|
||||||
|
+ (CLLocationDistance)CLLocationDistance:(id)json;
|
||||||
|
+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json;
|
||||||
|
|
||||||
|
@end
|
||||||
25
React/Views/RCTConvert+CoreLocation.m
Normal file
25
React/Views/RCTConvert+CoreLocation.m
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// RCTConvert+CoreLocation.m
|
||||||
|
// React
|
||||||
|
//
|
||||||
|
// Created by Nick Lockwood on 12/04/2015.
|
||||||
|
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "RCTConvert+CoreLocation.h"
|
||||||
|
|
||||||
|
@implementation RCTConvert(CoreLocation)
|
||||||
|
|
||||||
|
RCT_CONVERTER(CLLocationDegrees, CLLocationDegrees, doubleValue);
|
||||||
|
RCT_CONVERTER(CLLocationDistance, CLLocationDistance, doubleValue);
|
||||||
|
|
||||||
|
+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json
|
||||||
|
{
|
||||||
|
json = [self NSDictionary:json];
|
||||||
|
return (CLLocationCoordinate2D){
|
||||||
|
[self CLLocationDegrees:json[@"latitude"]],
|
||||||
|
[self CLLocationDegrees:json[@"longitude"]]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
22
React/Views/RCTConvert+MapKit.h
Normal file
22
React/Views/RCTConvert+MapKit.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// RCTConvert+MapKit.h
|
||||||
|
// React
|
||||||
|
//
|
||||||
|
// Created by Nick Lockwood on 12/04/2015.
|
||||||
|
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <MapKit/MapKit.h>
|
||||||
|
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
|
||||||
|
@interface RCTConvert (MapKit)
|
||||||
|
|
||||||
|
+ (MKCoordinateSpan)MKCoordinateSpan:(id)json;
|
||||||
|
+ (MKCoordinateRegion)MKCoordinateRegion:(id)json;
|
||||||
|
+ (MKShape *)MKShape:(id)json;
|
||||||
|
|
||||||
|
typedef NSArray MKShapeArray;
|
||||||
|
+ (MKShapeArray *)MKShapeArray:(id)json;
|
||||||
|
|
||||||
|
@end
|
||||||
46
React/Views/RCTConvert+MapKit.m
Normal file
46
React/Views/RCTConvert+MapKit.m
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// RCTConvert+MapKit.m
|
||||||
|
// React
|
||||||
|
//
|
||||||
|
// Created by Nick Lockwood on 12/04/2015.
|
||||||
|
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "RCTConvert+MapKit.h"
|
||||||
|
|
||||||
|
#import "RCTConvert+CoreLocation.h"
|
||||||
|
|
||||||
|
@implementation RCTConvert(MapKit)
|
||||||
|
|
||||||
|
+ (MKCoordinateSpan)MKCoordinateSpan:(id)json
|
||||||
|
{
|
||||||
|
json = [self NSDictionary:json];
|
||||||
|
return (MKCoordinateSpan){
|
||||||
|
[self CLLocationDegrees:json[@"latitudeDelta"]],
|
||||||
|
[self CLLocationDegrees:json[@"longitudeDelta"]]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (MKCoordinateRegion)MKCoordinateRegion:(id)json
|
||||||
|
{
|
||||||
|
return (MKCoordinateRegion){
|
||||||
|
[self CLLocationCoordinate2D:json],
|
||||||
|
[self MKCoordinateSpan:json]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (MKShape *)MKShape:(id)json
|
||||||
|
{
|
||||||
|
json = [self NSDictionary:json];
|
||||||
|
|
||||||
|
// TODO: more shape types
|
||||||
|
MKShape *shape = [[MKPointAnnotation alloc] init];
|
||||||
|
shape.coordinate = [self CLLocationCoordinate2D:json];
|
||||||
|
shape.title = [RCTConvert NSString:json[@"title"]];
|
||||||
|
shape.subtitle = [RCTConvert NSString:json[@"subtitle"]];
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_ARRAY_CONVERTER(MKShape)
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
#import <MapKit/MapKit.h>
|
#import <MapKit/MapKit.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "RCTConvert+MapKit.h"
|
||||||
|
|
||||||
extern const CLLocationDegrees RCTMapDefaultSpan;
|
extern const CLLocationDegrees RCTMapDefaultSpan;
|
||||||
extern const NSTimeInterval RCTMapRegionChangeObserveInterval;
|
extern const NSTimeInterval RCTMapRegionChangeObserveInterval;
|
||||||
extern const CGFloat RCTMapZoomBoundBuffer;
|
extern const CGFloat RCTMapZoomBoundBuffer;
|
||||||
@@ -19,9 +21,12 @@ extern const CGFloat RCTMapZoomBoundBuffer;
|
|||||||
@interface RCTMap: MKMapView
|
@interface RCTMap: MKMapView
|
||||||
|
|
||||||
@property (nonatomic, assign) BOOL followUserLocation;
|
@property (nonatomic, assign) BOOL followUserLocation;
|
||||||
|
@property (nonatomic, assign) BOOL hasStartedLoading;
|
||||||
@property (nonatomic, assign) CGFloat minDelta;
|
@property (nonatomic, assign) CGFloat minDelta;
|
||||||
@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;
|
||||||
|
|
||||||
|
- (void)setAnnotations:(MKShapeArray *)annotations;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#import "RCTMap.h"
|
#import "RCTMap.h"
|
||||||
|
|
||||||
#import "RCTConvert.h"
|
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
@@ -27,10 +26,14 @@ const CGFloat RCTMapZoomBoundBuffer = 0.01;
|
|||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
|
||||||
|
_hasStartedLoading = NO;
|
||||||
|
|
||||||
// Find Apple link label
|
// Find Apple link label
|
||||||
for (UIView *subview in self.subviews) {
|
for (UIView *subview in self.subviews) {
|
||||||
if ([NSStringFromClass(subview.class) isEqualToString:@"MKAttributionLabel"]) {
|
if ([NSStringFromClass(subview.class) isEqualToString:@"MKAttributionLabel"]) {
|
||||||
// This check is super hacky, but the whole premise of moving around Apple's internal subviews is super hacky
|
// This check is super hacky, but the whole premise of moving around
|
||||||
|
// Apple's internal subviews is super hacky
|
||||||
_legalLabel = subview;
|
_legalLabel = subview;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -82,11 +85,11 @@ const CGFloat RCTMapZoomBoundBuffer = 0.01;
|
|||||||
[_locationManager requestWhenInUseAuthorization];
|
[_locationManager requestWhenInUseAuthorization];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[super setShowsUserLocation:showsUserLocation];
|
super.showsUserLocation = showsUserLocation;
|
||||||
|
|
||||||
// If it needs to show user location, force map view centered
|
// If it needs to show user location, force map view centered
|
||||||
// on user's current location on user location updates
|
// on user's current location on user location updates
|
||||||
self.followUserLocation = showsUserLocation;
|
_followUserLocation = showsUserLocation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,4 +112,12 @@ const CGFloat RCTMapZoomBoundBuffer = 0.01;
|
|||||||
[super setRegion:region animated:YES];
|
[super setRegion:region animated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setAnnotations:(MKShapeArray *)annotations
|
||||||
|
{
|
||||||
|
[self removeAnnotations:self.annotations];
|
||||||
|
if (annotations.count) {
|
||||||
|
[self addAnnotations:annotations];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,43 +10,13 @@
|
|||||||
#import "RCTMapManager.h"
|
#import "RCTMapManager.h"
|
||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTConvert+CoreLocation.h"
|
||||||
|
#import "RCTConvert+MapKit.h"
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTMap.h"
|
#import "RCTMap.h"
|
||||||
#import "UIView+React.h"
|
#import "UIView+React.h"
|
||||||
|
|
||||||
@implementation RCTConvert(CoreLocation)
|
static NSString *const RCTMapViewKey = @"MapView";
|
||||||
|
|
||||||
+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json
|
|
||||||
{
|
|
||||||
json = [self NSDictionary:json];
|
|
||||||
return (CLLocationCoordinate2D){
|
|
||||||
[self double:json[@"latitude"]],
|
|
||||||
[self double:json[@"longitude"]]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation RCTConvert(MapKit)
|
|
||||||
|
|
||||||
+ (MKCoordinateSpan)MKCoordinateSpan:(id)json
|
|
||||||
{
|
|
||||||
json = [self NSDictionary:json];
|
|
||||||
return (MKCoordinateSpan){
|
|
||||||
[self double:json[@"latitudeDelta"]],
|
|
||||||
[self double:json[@"longitudeDelta"]]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (MKCoordinateRegion)MKCoordinateRegion:(id)json
|
|
||||||
{
|
|
||||||
return (MKCoordinateRegion){
|
|
||||||
[self CLLocationCoordinate2D:json],
|
|
||||||
[self MKCoordinateSpan:json]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface RCTMapManager() <MKMapViewDelegate>
|
@interface RCTMapManager() <MKMapViewDelegate>
|
||||||
|
|
||||||
@@ -72,6 +42,8 @@ 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(region, MKCoordinateRegion)
|
RCT_EXPORT_VIEW_PROPERTY(region, MKCoordinateRegion)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(annotations, MKShapeArray)
|
||||||
|
|
||||||
|
|
||||||
#pragma mark MKMapViewDelegate
|
#pragma mark MKMapViewDelegate
|
||||||
|
|
||||||
@@ -93,12 +65,15 @@ RCT_EXPORT_VIEW_PROPERTY(region, MKCoordinateRegion)
|
|||||||
{
|
{
|
||||||
[self _regionChanged:mapView];
|
[self _regionChanged:mapView];
|
||||||
|
|
||||||
mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTMapRegionChangeObserveInterval
|
if (animated) {
|
||||||
target:self
|
mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTMapRegionChangeObserveInterval
|
||||||
selector:@selector(_onTick:)
|
target:self
|
||||||
userInfo:@{ @"mapView": mapView }
|
selector:@selector(_onTick:)
|
||||||
repeats:YES];
|
userInfo:@{ RCTMapViewKey: mapView }
|
||||||
[[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes];
|
repeats:YES];
|
||||||
|
|
||||||
|
[[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(BOOL)animated
|
- (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(BOOL)animated
|
||||||
@@ -107,6 +82,17 @@ RCT_EXPORT_VIEW_PROPERTY(region, MKCoordinateRegion)
|
|||||||
mapView.regionChangeObserveTimer = nil;
|
mapView.regionChangeObserveTimer = nil;
|
||||||
|
|
||||||
[self _regionChanged:mapView];
|
[self _regionChanged:mapView];
|
||||||
|
|
||||||
|
// Don't send region did change events until map has
|
||||||
|
// started loading, as these won't represent the final location
|
||||||
|
if (mapView.hasStartedLoading) {
|
||||||
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)mapViewWillStartLoadingMap:(RCTMap *)mapView
|
||||||
|
{
|
||||||
|
mapView.hasStartedLoading = YES;
|
||||||
[self _emitRegionChangeEvent:mapView continuous:NO];
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +100,7 @@ RCT_EXPORT_VIEW_PROPERTY(region, MKCoordinateRegion)
|
|||||||
|
|
||||||
- (void)_onTick:(NSTimer *)timer
|
- (void)_onTick:(NSTimer *)timer
|
||||||
{
|
{
|
||||||
[self _regionChanged:timer.userInfo[@"mapView"]];
|
[self _regionChanged:timer.userInfo[RCTMapViewKey]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_regionChanged:(RCTMap *)mapView
|
- (void)_regionChanged:(RCTMap *)mapView
|
||||||
|
|||||||
@@ -494,17 +494,20 @@ NSInteger kNeverProgressed = -10000;
|
|||||||
jsMakingNoProgressAndDoesntNeedTo)) {
|
jsMakingNoProgressAndDoesntNeedTo)) {
|
||||||
RCTLogError(@"JS has only made partial progress to catch up to UIKit");
|
RCTLogError(@"JS has only made partial progress to catch up to UIKit");
|
||||||
}
|
}
|
||||||
RCTAssert(
|
if (currentReactCount > _currentViews.count) {
|
||||||
currentReactCount <= _currentViews.count,
|
RCTLogError(@"Cannot adjust current top of stack beyond available views");
|
||||||
@"Cannot adjust current top of stack beyond available views"
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// Views before the previous react count must not have changed. Views greater than previousReactCount
|
// Views before the previous react count must not have changed. Views greater than previousReactCount
|
||||||
// up to currentReactCount may have changed.
|
// up to currentReactCount may have changed.
|
||||||
for (NSInteger i = 0; i < MIN(_currentViews.count, MIN(_previousViews.count, previousReactCount)); i++) {
|
for (NSInteger i = 0; i < MIN(_currentViews.count, MIN(_previousViews.count, previousReactCount)); i++) {
|
||||||
RCTAssert(_currentViews[i] == _previousViews[i], @"current view should equal previous view");
|
if (_currentViews[i] != _previousViews[i]) {
|
||||||
|
RCTLogError(@"current view should equal previous view");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentReactCount < 1) {
|
||||||
|
RCTLogError(@"should be at least one current view");
|
||||||
}
|
}
|
||||||
RCTAssert(currentReactCount >= 1, @"should be at least one current view");
|
|
||||||
if (jsGettingAhead) {
|
if (jsGettingAhead) {
|
||||||
if (reactPushOne) {
|
if (reactPushOne) {
|
||||||
UIView *lastView = [_currentViews lastObject];
|
UIView *lastView = [_currentViews lastObject];
|
||||||
@@ -517,7 +520,7 @@ NSInteger kNeverProgressed = -10000;
|
|||||||
_numberOfViewControllerMovesToIgnore = viewControllerCount - currentReactCount;
|
_numberOfViewControllerMovesToIgnore = viewControllerCount - currentReactCount;
|
||||||
[_navigationController popToViewController:viewControllerToPopTo animated:YES];
|
[_navigationController popToViewController:viewControllerToPopTo animated:YES];
|
||||||
} else {
|
} else {
|
||||||
RCTAssert(NO, @"Pushing or popping more than one view at a time from JS");
|
RCTLogError(@"Pushing or popping more than one view at a time from JS");
|
||||||
}
|
}
|
||||||
} else if (jsCatchingUp) {
|
} else if (jsCatchingUp) {
|
||||||
[self freeLock]; // Nothing to push/pop
|
[self freeLock]; // Nothing to push/pop
|
||||||
|
|||||||
@@ -31,20 +31,19 @@
|
|||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
systemIcons = @{
|
systemIcons = @{
|
||||||
@"bookmarks": @(UITabBarSystemItemBookmarks),
|
@"bookmarks": @(UITabBarSystemItemBookmarks),
|
||||||
@"contacts": @(UITabBarSystemItemContacts),
|
@"contacts": @(UITabBarSystemItemContacts),
|
||||||
@"downloads": @(UITabBarSystemItemDownloads),
|
@"downloads": @(UITabBarSystemItemDownloads),
|
||||||
@"favorites": @(UITabBarSystemItemFavorites),
|
@"favorites": @(UITabBarSystemItemFavorites),
|
||||||
@"featured": @(UITabBarSystemItemFeatured),
|
@"featured": @(UITabBarSystemItemFeatured),
|
||||||
@"history": @(UITabBarSystemItemHistory),
|
@"history": @(UITabBarSystemItemHistory),
|
||||||
@"more": @(UITabBarSystemItemMore),
|
@"more": @(UITabBarSystemItemMore),
|
||||||
@"most-recent": @(UITabBarSystemItemMostRecent),
|
@"most-recent": @(UITabBarSystemItemMostRecent),
|
||||||
@"most-viewed": @(UITabBarSystemItemMostViewed),
|
@"most-viewed": @(UITabBarSystemItemMostViewed),
|
||||||
@"recents": @(UITabBarSystemItemRecents),
|
@"recents": @(UITabBarSystemItemRecents),
|
||||||
@"search": @(UITabBarSystemItemSearch),
|
@"search": @(UITabBarSystemItemSearch),
|
||||||
@"top-rated": @(UITabBarSystemItemTopRated),
|
@"top-rated": @(UITabBarSystemItemTopRated),
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update icon
|
// Update icon
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
@property (nonatomic, assign) BOOL caretHidden;
|
@property (nonatomic, assign) BOOL caretHidden;
|
||||||
@property (nonatomic, assign) BOOL autoCorrect;
|
@property (nonatomic, assign) BOOL autoCorrect;
|
||||||
|
@property (nonatomic, assign) BOOL selectTextOnFocus;
|
||||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||||
|
|||||||
@@ -104,15 +104,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCT_TEXT_EVENT_HANDLER(_textFieldDidChange, RCTTextEventTypeChange)
|
RCT_TEXT_EVENT_HANDLER(_textFieldDidChange, RCTTextEventTypeChange)
|
||||||
RCT_TEXT_EVENT_HANDLER(_textFieldBeginEditing, RCTTextEventTypeFocus)
|
|
||||||
RCT_TEXT_EVENT_HANDLER(_textFieldEndEditing, RCTTextEventTypeEnd)
|
RCT_TEXT_EVENT_HANDLER(_textFieldEndEditing, RCTTextEventTypeEnd)
|
||||||
RCT_TEXT_EVENT_HANDLER(_textFieldSubmitEditing, RCTTextEventTypeSubmit)
|
RCT_TEXT_EVENT_HANDLER(_textFieldSubmitEditing, RCTTextEventTypeSubmit)
|
||||||
|
|
||||||
|
- (void)_textFieldBeginEditing
|
||||||
|
{
|
||||||
|
if (_selectTextOnFocus) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self selectAll:nil];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
|
||||||
|
reactTag:self.reactTag
|
||||||
|
text:self.text];
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: we should support shouldChangeTextInRect (see UITextFieldDelegate)
|
// TODO: we should support shouldChangeTextInRect (see UITextFieldDelegate)
|
||||||
|
|
||||||
- (BOOL)becomeFirstResponder
|
- (BOOL)becomeFirstResponder
|
||||||
{
|
{
|
||||||
_jsRequestingFirstResponder = YES; // TODO: is this still needed?
|
_jsRequestingFirstResponder = YES;
|
||||||
BOOL result = [super becomeFirstResponder];
|
BOOL result = [super becomeFirstResponder];
|
||||||
_jsRequestingFirstResponder = NO;
|
_jsRequestingFirstResponder = NO;
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(clearButtonMode, UITextFieldViewMode)
|
RCT_EXPORT_VIEW_PROPERTY(clearButtonMode, UITextFieldViewMode)
|
||||||
|
RCT_REMAP_VIEW_PROPERTY(clearTextOnFocus, clearsOnBeginEditing, BOOL)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType)
|
RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType)
|
RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL)
|
RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL)
|
||||||
|
|||||||
Reference in New Issue
Block a user