mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-05-20 05:48:44 +08:00
Updates from Tue 8 Sep
This commit is contained in:
@@ -503,6 +503,12 @@ type ValueListenerCallback = (state: {value: number}) => void;
|
||||
|
||||
var _uniqueId = 1;
|
||||
|
||||
/**
|
||||
* Standard value for driving animations. One `Animated.Value` can drive
|
||||
* multiple properties in a synchronized fashion, but can only be driven by one
|
||||
* mechanism at a time. Using a new mechanism (e.g. starting a new animation,
|
||||
* or calling `setValue`) will stop any previous ones.
|
||||
*/
|
||||
class AnimatedValue extends AnimatedWithChildren {
|
||||
_value: number;
|
||||
_offset: number;
|
||||
@@ -526,6 +532,10 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
return this._value + this._offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the value. This will stop any animations running on the value
|
||||
* and udpate all the bound properties.
|
||||
*/
|
||||
setValue(value: number): void {
|
||||
if (this._animation) {
|
||||
this._animation.stop();
|
||||
@@ -534,15 +544,29 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
this._updateValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an offset that is applied on top of whatever value is set, whether via
|
||||
* `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||
* things like the start of a pan gesture.
|
||||
*/
|
||||
setOffset(offset: number): void {
|
||||
this._offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the offset value into the base value and resets the offset to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*/
|
||||
flattenOffset(): void {
|
||||
this._value += this._offset;
|
||||
this._offset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an asynchronous listener to the value so you can observe updates from
|
||||
* animations or whathaveyou. This is useful because there is no way to
|
||||
* syncronously read the value because it might be driven natively.
|
||||
*/
|
||||
addListener(callback: ValueListenerCallback): string {
|
||||
var id = String(_uniqueId++);
|
||||
this._listeners[id] = callback;
|
||||
@@ -557,6 +581,30 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* final value after stopping the animation, which is useful for updating
|
||||
* state to match the animation position with layout.
|
||||
*/
|
||||
stopAnimation(callback?: ?(value: number) => void): void {
|
||||
this.stopTracking();
|
||||
this._animation && this._animation.stop();
|
||||
this._animation = null;
|
||||
callback && callback(this.__getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates the value before updating the property, e.g. mapping 0-1 to
|
||||
* 0-10.
|
||||
*/
|
||||
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
|
||||
return new AnimatedInterpolation(this, Interpolation.create(config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically only used internally, but could be used by a custom Animation
|
||||
* class.
|
||||
*/
|
||||
animate(animation: Animation, callback: ?EndCallback): void {
|
||||
var handle = InteractionManager.createInteractionHandle();
|
||||
var previousAnimation = this._animation;
|
||||
@@ -576,27 +624,22 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
);
|
||||
}
|
||||
|
||||
stopAnimation(callback?: ?(value: number) => void): void {
|
||||
this.stopTracking();
|
||||
this._animation && this._animation.stop();
|
||||
this._animation = null;
|
||||
callback && callback(this.__getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically only used internally.
|
||||
*/
|
||||
stopTracking(): void {
|
||||
this._tracking && this._tracking.__detach();
|
||||
this._tracking = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically only used internally.
|
||||
*/
|
||||
track(tracking: Animated): void {
|
||||
this.stopTracking();
|
||||
this._tracking = tracking;
|
||||
}
|
||||
|
||||
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
|
||||
return new AnimatedInterpolation(this, Interpolation.create(config));
|
||||
}
|
||||
|
||||
_updateValue(value: number): void {
|
||||
this._value = value;
|
||||
_flush(this);
|
||||
@@ -607,6 +650,45 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
type ValueXYListenerCallback = (value: {x: number; y: number}) => void;
|
||||
|
||||
/**
|
||||
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
|
||||
* API to normal `Animated.Value`, but multiplexed. Contains two regular
|
||||
* `Animated.Value`s under the hood. Example:
|
||||
*
|
||||
*```javascript
|
||||
* class DraggableView extends React.Component {
|
||||
* constructor(props) {
|
||||
* super(props);
|
||||
* this.state = {
|
||||
* pan: new Animated.ValueXY(), // inits to zero
|
||||
* };
|
||||
* this.state.panResponder = PanResponder.create({
|
||||
* onStartShouldSetPanResponder: () => true,
|
||||
* onPanResponderMove: Animated.event([null, {
|
||||
* dx: this.state.pan.x, // x,y are Animated.Value
|
||||
* dy: this.state.pan.y,
|
||||
* }]),
|
||||
* onPanResponderRelease: () => {
|
||||
* Animated.spring(
|
||||
* this.state.pan, // Auto-multiplexed
|
||||
* {toValue: {x: 0, y: 0}} // Back to zero
|
||||
* ).start();
|
||||
* },
|
||||
* });
|
||||
* }
|
||||
* render() {
|
||||
* return (
|
||||
* <Animated.View
|
||||
* {...this.state.panResponder.panHandlers}
|
||||
* style={this.state.pan.getLayout()}>
|
||||
* {this.props.children}
|
||||
* </Animated.View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*```
|
||||
*/
|
||||
class AnimatedValueXY extends AnimatedWithChildren {
|
||||
x: AnimatedValue;
|
||||
y: AnimatedValue;
|
||||
@@ -677,6 +759,13 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
delete this._listeners[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `{x, y}` into `{left, top}` for use in style, e.g.
|
||||
*
|
||||
*```javascript
|
||||
* style={this.state.anim.getLayout()}
|
||||
*```
|
||||
*/
|
||||
getLayout(): {[key: string]: AnimatedValue} {
|
||||
return {
|
||||
left: this.x,
|
||||
@@ -684,6 +773,15 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `{x, y}` into a useable translation transform, e.g.
|
||||
*
|
||||
*```javascript
|
||||
* style={{
|
||||
* transform: this.state.anim.getTranslateTransform()
|
||||
* }}
|
||||
*```
|
||||
*/
|
||||
getTranslateTransform(): Array<{[key: string]: AnimatedValue}> {
|
||||
return [
|
||||
{translateX: this.x},
|
||||
@@ -1235,21 +1333,6 @@ var stagger = function(
|
||||
|
||||
type Mapping = {[key: string]: Mapping} | AnimatedValue;
|
||||
|
||||
/**
|
||||
* Takes an array of mappings and extracts values from each arg accordingly,
|
||||
* then calls setValue on the mapped outputs. e.g.
|
||||
*
|
||||
* onScroll={this.AnimatedEvent(
|
||||
* [{nativeEvent: {contentOffset: {x: this._scrollX}}}]
|
||||
* {listener} // optional listener invoked asynchronously
|
||||
* )
|
||||
* ...
|
||||
* onPanResponderMove: this.AnimatedEvent([
|
||||
* null, // raw event arg
|
||||
* {dx: this._panX}, // gestureState arg
|
||||
* ]),
|
||||
*
|
||||
*/
|
||||
type EventConfig = {listener?: ?Function};
|
||||
var event = function(
|
||||
argMapping: Array<?Mapping>,
|
||||
@@ -1287,23 +1370,179 @@ var event = function(
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Animations are an important part of modern UX, and the `Animated`
|
||||
* library is designed to make them fluid, powerful, and easy to build and
|
||||
* maintain.
|
||||
*
|
||||
* The simplest workflow is to create an `Animated.Value`, hook it up to one or
|
||||
* more style attributes of an animated component, and then drive updates either
|
||||
* via animations, such as `Animated.timing`, or by hooking into gestures like
|
||||
* panning or scolling via `Animated.event`. `Animated.Value` can also bind to
|
||||
* props other than style, and can be interpolated as well. Here is a basic
|
||||
* example of a container view that will fade in when it's mounted:
|
||||
*
|
||||
*```javascript
|
||||
* class FadeInView extends React.Component {
|
||||
* constructor(props) {
|
||||
* super(props);
|
||||
* this.state = {
|
||||
* fadeAnim: new Animated.Value(0), // init opacity 0
|
||||
* };
|
||||
* }
|
||||
* componentDidMount() {
|
||||
* Animated.timing( // Uses easing functions
|
||||
* this.state.fadeAnim, // The value to drive
|
||||
* {toValue: 1}, // Configuration
|
||||
* ).start(); // Don't forget start!
|
||||
* }
|
||||
* render() {
|
||||
* return (
|
||||
* <Animated.View // Special animatable View
|
||||
* style={{opacity: this.state.fadeAnim}}> // Binds
|
||||
* {this.props.children}
|
||||
* </Animated.View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*```
|
||||
*
|
||||
* Note that only animatable components can be animated. `View`, `Text`, and
|
||||
* `Image` are already provided, and you can create custom ones with
|
||||
* `createAnimatedComponent`. These special components do the magic of binding
|
||||
* the animated values to the properties, and do targetted native updates to
|
||||
* avoid the cost of the react render and reconciliation process on every frame.
|
||||
* They also handle cleanup on unmount so they are safe by default.
|
||||
*
|
||||
* Animations are heavily configurable. Custom and pre-defined easing
|
||||
* functions, delays, durations, decay factors, spring constants, and more can
|
||||
* all be tweaked depending on the type of animation.
|
||||
*
|
||||
* A single `Animated.Value` can drive any number of properties, and each
|
||||
* property can be run through an interpolation first. An interpolation maps
|
||||
* input ranges to output ranges, typically using a linear interpolation but
|
||||
* also supports easing functions. By default, it will extrapolate the curve
|
||||
* beyond the ranges given, but you can also have it clamp the output value.
|
||||
*
|
||||
* For example, you may want to think about your `Animated.Value` as going from
|
||||
* 0 to 1, but animate the position from 150px to 0px and the opacity from 0 to
|
||||
* 1. This can easily be done by modifying `style` in the example above like so:
|
||||
*
|
||||
*```javascript
|
||||
* style={{
|
||||
* opacity: this.state.fadeAnim, // Binds directly
|
||||
* transform: [{
|
||||
* translateY: this.state.fadeAnim.interpolate({
|
||||
* inputRange: [0, 1],
|
||||
* outputRange: [150, 0] // 0 : 150, 0.5 : 75, 1 : 0
|
||||
* }),
|
||||
* }],
|
||||
* }}>
|
||||
*```
|
||||
*
|
||||
* Animations can also be combined in complex ways using composition functions
|
||||
* such as `sequence` and `parallel`, and can also be chained together simply
|
||||
* by setting the `toValue` of one animation to be another `Animated.Value`.
|
||||
*
|
||||
* `Animated.ValueXY` is handy for 2D animations, like panning, and there are
|
||||
* other helpful additions like `setOffset` and `getLayout` to aid with typical
|
||||
* interaction patterns, like drag-and-drop.
|
||||
*
|
||||
* You can see more example usage in `AnimationExample.js`, the Gratuitous
|
||||
* Animation App, and [Animations documentation guide](http://facebook.github.io/react-native/docs/animations.html).
|
||||
*
|
||||
* Note that `Animated` is designed to be fully serializable so that animations
|
||||
* can be run in a high performace way, independent of the normal JavaScript
|
||||
* event loop. This does influence the API, so keep that in mind when it seems a
|
||||
* little trickier to do something compared to a fully synchronous system.
|
||||
* Checkout `Animated.Value.addListener` as a way to work around some of these
|
||||
* limitations, but use it sparingly since it might have performance
|
||||
* implications in the future.
|
||||
*/
|
||||
module.exports = {
|
||||
delay,
|
||||
sequence,
|
||||
parallel,
|
||||
stagger,
|
||||
/**
|
||||
* Standard value class for driving animations. Typically initialized with
|
||||
* `new Animated.Value(0);`
|
||||
*/
|
||||
Value: AnimatedValue,
|
||||
/**
|
||||
* 2D value class for driving 2D animations, such as pan gestures.
|
||||
*/
|
||||
ValueXY: AnimatedValueXY,
|
||||
|
||||
/**
|
||||
* An animatable View component.
|
||||
*/
|
||||
View: createAnimatedComponent(View),
|
||||
/**
|
||||
* An animatable Text component.
|
||||
*/
|
||||
Text: createAnimatedComponent(Text),
|
||||
/**
|
||||
* An animatable Image component.
|
||||
*/
|
||||
Image: createAnimatedComponent(Image),
|
||||
|
||||
/**
|
||||
* Animates a value from an initial velocity to zero based on a decay
|
||||
* coefficient.
|
||||
*/
|
||||
decay,
|
||||
/**
|
||||
* Animates a value along a timed easing curve. The `Easing` module has tons
|
||||
* of pre-defined curves, or you can use your own function.
|
||||
*/
|
||||
timing,
|
||||
/**
|
||||
* Spring animation based on Rebound and Origami. Tracks velocity state to
|
||||
* create fluid motions as the `toValue` updates, and can be chained together.
|
||||
*/
|
||||
spring,
|
||||
|
||||
/**
|
||||
* Starts an animation after the given delay.
|
||||
*/
|
||||
delay,
|
||||
/**
|
||||
* Starts an array of animations in order, waiting for each to complete
|
||||
* before starting the next. If the current running animation is stopped, no
|
||||
* following animations will be started.
|
||||
*/
|
||||
sequence,
|
||||
/**
|
||||
* Starts an array of animations all at the same time. By default, if one
|
||||
* of the animations is stopped, they will all be stopped. You can override
|
||||
* this with the `stopTogether` flag.
|
||||
*/
|
||||
parallel,
|
||||
/**
|
||||
* Array of animations may run in parallel (overlap), but are started in
|
||||
* sequence with successive delays. Nice for doing trailing effects.
|
||||
*/
|
||||
stagger,
|
||||
|
||||
/**
|
||||
* Takes an array of mappings and extracts values from each arg accordingly,
|
||||
* then calls `setValue` on the mapped outputs. e.g.
|
||||
*
|
||||
*```javascript
|
||||
* onScroll={this.AnimatedEvent(
|
||||
* [{nativeEvent: {contentOffset: {x: this._scrollX}}}]
|
||||
* {listener}, // Optional async listener
|
||||
* )
|
||||
* ...
|
||||
* onPanResponderMove: this.AnimatedEvent([
|
||||
* null, // raw event arg ignored
|
||||
* {dx: this._panX}, // gestureState arg
|
||||
* ]),
|
||||
*```
|
||||
*/
|
||||
event,
|
||||
|
||||
Value: AnimatedValue,
|
||||
ValueXY: AnimatedValueXY,
|
||||
__PropsOnlyForTests: AnimatedProps,
|
||||
View: createAnimatedComponent(View),
|
||||
Text: createAnimatedComponent(Text),
|
||||
Image: createAnimatedComponent(Image),
|
||||
/**
|
||||
* Make any React component Animatable. Used to create `Animated.View`, etc.
|
||||
*/
|
||||
createAnimatedComponent,
|
||||
|
||||
__PropsOnlyForTests: AnimatedProps,
|
||||
};
|
||||
|
||||
@@ -123,12 +123,18 @@ class Easing {
|
||||
return easing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an easing function backwards.
|
||||
*/
|
||||
static out(
|
||||
easing: (t: number) => number,
|
||||
): (t: number) => number {
|
||||
return (t) => 1 - easing(1 - t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes any easing function symmetrical.
|
||||
*/
|
||||
static inOut(
|
||||
easing: (t: number) => number,
|
||||
): (t: number) => number {
|
||||
|
||||
296
Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj
Normal file
296
Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,296 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
|
||||
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; };
|
||||
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
58B5115B1A9E6B3D00147676 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = "<group>"; };
|
||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImagePickerManager.m; sourceTree = "<group>"; };
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = "<group>"; };
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
|
||||
58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTCameraRoll.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
58B5115A1A9E6B3D00147676 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
58B511541A9E6B3D00147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */,
|
||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */,
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
|
||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */,
|
||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */,
|
||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */,
|
||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */,
|
||||
58B5115E1A9E6B3D00147676 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
58B5115E1A9E6B3D00147676 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
58B5115C1A9E6B3D00147676 /* RCTCameraRoll */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTCameraRoll" */;
|
||||
buildPhases = (
|
||||
58B511591A9E6B3D00147676 /* Sources */,
|
||||
58B5115A1A9E6B3D00147676 /* Frameworks */,
|
||||
58B5115B1A9E6B3D00147676 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RCTCameraRoll;
|
||||
productName = RCTNetworkImage;
|
||||
productReference = 58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
58B511551A9E6B3D00147676 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
58B5115C1A9E6B3D00147676 = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTCameraRoll" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 58B511541A9E6B3D00147676;
|
||||
productRefGroup = 58B5115E1A9E6B3D00147676 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B5115C1A9E6B3D00147676 /* RCTCameraRoll */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
58B511591A9E6B3D00147676 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */,
|
||||
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */,
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
58B5116F1A9E6B3D00147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
WARNING_CFLAGS = (
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511701A9E6B3D00147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
WARNING_CFLAGS = (
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511721A9E6B3D00147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../Image/**",
|
||||
"$(SRCROOT)/../Network/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/UIExplorer-gjaibsjtheitasdxdtcvxxqavkvy/Build/Products/Debug-iphoneos",
|
||||
);
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTCameraRoll;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511731A9E6B3D00147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../Image/**",
|
||||
"$(SRCROOT)/../Network/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/UIExplorer-gjaibsjtheitasdxdtcvxxqavkvy/Build/Products/Debug-iphoneos",
|
||||
);
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTCameraRoll;
|
||||
RUN_CLANG_STATIC_ANALYZER = NO;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTCameraRoll" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B5116F1A9E6B3D00147676 /* Debug */,
|
||||
58B511701A9E6B3D00147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTCameraRoll" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511721A9E6B3D00147676 /* Debug */,
|
||||
58B511731A9E6B3D00147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 58B511551A9E6B3D00147676 /* Project object */;
|
||||
}
|
||||
@@ -49,6 +49,7 @@ var AndroidTextInputAttributes = {
|
||||
keyboardType: true,
|
||||
mostRecentEventCount: true,
|
||||
multiline: true,
|
||||
numberOfLines: true,
|
||||
password: true,
|
||||
placeholder: true,
|
||||
placeholderTextColor: true,
|
||||
@@ -193,6 +194,12 @@ var TextInput = React.createClass({
|
||||
* @platform ios
|
||||
*/
|
||||
maxLength: PropTypes.number,
|
||||
/**
|
||||
* Sets the number of lines for a TextInput. Use it with multiline set to
|
||||
* true to be able to fill the lines.
|
||||
* @platform android
|
||||
*/
|
||||
numberOfLines: PropTypes.number,
|
||||
/**
|
||||
* If true, the keyboard disables the return key when there is no text and
|
||||
* automatically enables it when there is text. The default value is false.
|
||||
@@ -484,6 +491,7 @@ var TextInput = React.createClass({
|
||||
keyboardType={this.props.keyboardType}
|
||||
mostRecentEventCount={this.state.mostRecentEventCount}
|
||||
multiline={this.props.multiline}
|
||||
numberOfLines={this.props.numberOfLines}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
|
||||
@@ -209,6 +209,8 @@ var TouchableHighlight = React.createClass({
|
||||
return (
|
||||
<View
|
||||
accessible={true}
|
||||
accessibilityComponentType={this.props.accessibilityComponentType}
|
||||
accessibilityTraits={this.props.accessibilityTraits}
|
||||
ref={UNDERLAY_REF}
|
||||
style={this.state.underlayStyle}
|
||||
onLayout={this.props.onLayout}
|
||||
|
||||
@@ -156,6 +156,8 @@ var TouchableOpacity = React.createClass({
|
||||
return (
|
||||
<Animated.View
|
||||
accessible={true}
|
||||
accessibilityComponentType={this.props.accessibilityComponentType}
|
||||
accessibilityTraits={this.props.accessibilityTraits}
|
||||
style={[this.props.style, {opacity: this.state.anim}]}
|
||||
testID={this.props.testID}
|
||||
onLayout={this.props.onLayout}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
var React = require('React');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var Touchable = require('Touchable');
|
||||
var View = require('View');
|
||||
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
@@ -36,11 +37,16 @@ var TouchableWithoutFeedback = React.createClass({
|
||||
mixins: [TimerMixin, Touchable.Mixin],
|
||||
|
||||
propTypes: {
|
||||
accessible: React.PropTypes.bool,
|
||||
accessibilityComponentType: React.PropTypes.oneOf(View.AccessibilityComponentType),
|
||||
accessibilityTraits: React.PropTypes.oneOfType([
|
||||
React.PropTypes.oneOf(View.AccessibilityTraits),
|
||||
React.PropTypes.arrayOf(React.PropTypes.oneOf(View.AccessibilityTraits)),
|
||||
]),
|
||||
/**
|
||||
* Called when the touch is released, but not if cancelled (e.g. by a scroll
|
||||
* that steals the responder lock).
|
||||
*/
|
||||
accessible: React.PropTypes.bool,
|
||||
onPress: React.PropTypes.func,
|
||||
onPressIn: React.PropTypes.func,
|
||||
onPressOut: React.PropTypes.func,
|
||||
@@ -120,6 +126,8 @@ var TouchableWithoutFeedback = React.createClass({
|
||||
// Note(avik): remove dynamic typecast once Flow has been upgraded
|
||||
return (React: any).cloneElement(onlyChild(this.props.children), {
|
||||
accessible: this.props.accessible !== false,
|
||||
accessibilityComponentType: this.props.accessibilityComponentType,
|
||||
accessibilityTraits: this.props.accessibilityTraits,
|
||||
testID: this.props.testID,
|
||||
onLayout: this.props.onLayout,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
|
||||
@@ -44,6 +44,13 @@ var AccessibilityTraits = [
|
||||
'pageTurn',
|
||||
];
|
||||
|
||||
var AccessibilityComponentType = [
|
||||
'none',
|
||||
'button',
|
||||
'radiobutton_checked',
|
||||
'radiobutton_unchecked',
|
||||
];
|
||||
|
||||
/**
|
||||
* The most fundamental component for building UI, `View` is a
|
||||
* container that supports layout with flexbox, style, some touch handling, and
|
||||
@@ -76,6 +83,11 @@ var View = React.createClass({
|
||||
validAttributes: ReactNativeViewAttributes.RCTView
|
||||
},
|
||||
|
||||
statics: {
|
||||
AccessibilityTraits,
|
||||
AccessibilityComponentType,
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
/**
|
||||
* When true, indicates that the view is an accessibility element. By default,
|
||||
@@ -95,12 +107,7 @@ var View = React.createClass({
|
||||
* native one. Works for Android only.
|
||||
* @platform android
|
||||
*/
|
||||
accessibilityComponentType: PropTypes.oneOf([
|
||||
'none',
|
||||
'button',
|
||||
'radiobutton_checked',
|
||||
'radiobutton_unchecked',
|
||||
]),
|
||||
accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentType),
|
||||
|
||||
/**
|
||||
* Indicates to accessibility services whether the user should be notified
|
||||
@@ -152,7 +159,7 @@ var View = React.createClass({
|
||||
* When `accessible` is true, the system will try to invoke this function
|
||||
* when the user performs accessibility tap gesture.
|
||||
*/
|
||||
onAcccessibilityTap: PropTypes.func,
|
||||
onAccessibilityTap: PropTypes.func,
|
||||
|
||||
/**
|
||||
* When `accessible` is true, the system will invoke this function when the
|
||||
|
||||
@@ -72,7 +72,9 @@ function setupDevtools() {
|
||||
}
|
||||
|
||||
function handleMessage(evt) {
|
||||
var data;
|
||||
// It's hard to handle JSON in a safe manner without inspecting it at
|
||||
// runtime, hence the any
|
||||
var data: any;
|
||||
try {
|
||||
data = JSON.parse(evt.data);
|
||||
} catch (e) {
|
||||
|
||||
@@ -27,60 +27,85 @@ RCT_EXPORT_MODULE()
|
||||
return !strcmp(header, "GIF87a") || !strcmp(header, "GIF89a");
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
{
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
|
||||
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL);
|
||||
NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue];
|
||||
|
||||
UIImage *image = nil;
|
||||
size_t imageCount = CGImageSourceGetCount(imageSource);
|
||||
NSTimeInterval duration = 0;
|
||||
NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount];
|
||||
NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
|
||||
for (size_t i = 0; i < imageCount; i++) {
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
|
||||
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
|
||||
NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary];
|
||||
if (imageCount > 1) {
|
||||
|
||||
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
|
||||
NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime];
|
||||
if (delayTime == nil) {
|
||||
if (i == 0) {
|
||||
delayTime = @(kDelayTimeIntervalDefault);
|
||||
} else {
|
||||
delayTime = delays[i - 1];
|
||||
NSTimeInterval duration = 0;
|
||||
NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount];
|
||||
NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
|
||||
for (size_t i = 0; i < imageCount; i++) {
|
||||
|
||||
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
|
||||
if (!image) {
|
||||
image = [UIImage imageWithCGImage:imageRef];
|
||||
}
|
||||
|
||||
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
|
||||
NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary];
|
||||
|
||||
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
|
||||
NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime];
|
||||
if (delayTime == nil) {
|
||||
if (i == 0) {
|
||||
delayTime = @(kDelayTimeIntervalDefault);
|
||||
} else {
|
||||
delayTime = delays[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
const NSTimeInterval kDelayTimeIntervalMinimum = 0.02;
|
||||
if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) {
|
||||
delayTime = @(kDelayTimeIntervalDefault);
|
||||
}
|
||||
|
||||
duration += delayTime.doubleValue;
|
||||
delays[i] = delayTime;
|
||||
images[i] = (__bridge_transfer id)imageRef;
|
||||
}
|
||||
CFRelease(imageSource);
|
||||
|
||||
NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count];
|
||||
NSTimeInterval runningDuration = 0;
|
||||
for (NSNumber *delayNumber in delays) {
|
||||
[keyTimes addObject:@(runningDuration / duration)];
|
||||
runningDuration += delayNumber.doubleValue;
|
||||
}
|
||||
|
||||
const NSTimeInterval kDelayTimeIntervalMinimum = 0.02;
|
||||
if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) {
|
||||
delayTime = @(kDelayTimeIntervalDefault);
|
||||
[keyTimes addObject:@1.0];
|
||||
|
||||
// Create animation
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
|
||||
animation.calculationMode = kCAAnimationDiscrete;
|
||||
animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount;
|
||||
animation.keyTimes = keyTimes;
|
||||
animation.values = images;
|
||||
animation.duration = duration;
|
||||
image.reactKeyframeAnimation = animation;
|
||||
|
||||
} else {
|
||||
|
||||
// Don't bother creating an animation
|
||||
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
|
||||
if (imageRef) {
|
||||
image = [UIImage imageWithCGImage:imageRef];
|
||||
CFRelease(imageRef);
|
||||
}
|
||||
|
||||
duration += delayTime.doubleValue;
|
||||
delays[i] = delayTime;
|
||||
images[i] = (__bridge_transfer id)image;
|
||||
}
|
||||
CFRelease(imageSource);
|
||||
|
||||
NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count];
|
||||
NSTimeInterval runningDuration = 0;
|
||||
for (NSNumber *delayNumber in delays) {
|
||||
[keyTimes addObject:@(runningDuration / duration)];
|
||||
runningDuration += delayNumber.doubleValue;
|
||||
CFRelease(imageSource);
|
||||
}
|
||||
|
||||
[keyTimes addObject:@1.0];
|
||||
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
|
||||
animation.calculationMode = kCAAnimationDiscrete;
|
||||
animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount;
|
||||
animation.keyTimes = keyTimes;
|
||||
animation.values = images;
|
||||
animation.duration = duration;
|
||||
completionHandler(nil, animation);
|
||||
|
||||
return nil;
|
||||
completionHandler(nil, image);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,14 +11,10 @@
|
||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
|
||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; };
|
||||
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
|
||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
|
||||
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; };
|
||||
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; };
|
||||
83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -43,10 +39,6 @@
|
||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = "<group>"; };
|
||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = "<group>"; };
|
||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
|
||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = "<group>"; };
|
||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImagePickerManager.m; sourceTree = "<group>"; };
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = "<group>"; };
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
|
||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
|
||||
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = "<group>"; };
|
||||
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageStoreManager.h; sourceTree = "<group>"; };
|
||||
@@ -56,10 +48,6 @@
|
||||
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
|
||||
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
|
||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||
83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetBundleImageLoader.h; sourceTree = "<group>"; };
|
||||
83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetBundleImageLoader.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -80,10 +68,6 @@
|
||||
children = (
|
||||
83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */,
|
||||
83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */,
|
||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */,
|
||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */,
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
|
||||
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
|
||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
||||
@@ -92,8 +76,6 @@
|
||||
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
|
||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
|
||||
143879371AAD32A300F088A5 /* RCTImageLoader.m */,
|
||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */,
|
||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */,
|
||||
1304D5A71AA8C4A30002E2BE /* RCTImageView.h */,
|
||||
1304D5A81AA8C4A30002E2BE /* RCTImageView.m */,
|
||||
1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */,
|
||||
@@ -102,8 +84,6 @@
|
||||
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
|
||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
|
||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
|
||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */,
|
||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */,
|
||||
58B5115E1A9E6B3D00147676 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
@@ -174,14 +154,10 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */,
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
||||
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */,
|
||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
|
||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
|
||||
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
|
||||
|
||||
@@ -50,7 +50,7 @@ RCT_EXPORT_MODULE()
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url
|
||||
progressHandler:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionBlock
|
||||
completionHandler:(void (^)(NSError *error, NSData *data))completionBlock
|
||||
{
|
||||
if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) {
|
||||
RCTLogError(@"You need to import the RCTNetworking library in order to download remote images.");
|
||||
|
||||
@@ -15,10 +15,15 @@
|
||||
@class ALAssetsLibrary;
|
||||
|
||||
typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total);
|
||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* UIImage or CAAnimation */);
|
||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* NSData, UIImage, CAAnimation */);
|
||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, UIImage *image);
|
||||
typedef void (^RCTImageLoaderCancellationBlock)(void);
|
||||
|
||||
@interface UIImage (React)
|
||||
|
||||
@property (nonatomic, copy) CAKeyframeAnimation *reactKeyframeAnimation;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTImageLoader : NSObject <RCTBridgeModule, RCTURLRequestHandler>
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,20 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr
|
||||
}
|
||||
}
|
||||
|
||||
@implementation UIImage (React)
|
||||
|
||||
- (CAKeyframeAnimation *)reactKeyframeAnimation
|
||||
{
|
||||
return objc_getAssociatedObject(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setReactKeyframeAnimation:(CAKeyframeAnimation *)reactKeyframeAnimation
|
||||
{
|
||||
objc_setAssociatedObject(self, @selector(reactKeyframeAnimation), reactKeyframeAnimation, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTImageLoader
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@@ -99,7 +113,7 @@ RCT_EXPORT_MODULE()
|
||||
progressBlock(progress, total);
|
||||
});
|
||||
}
|
||||
} completionHandler:^(NSError *error, id image) {
|
||||
} completionHandler:^(NSError *error, UIImage *image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
}] ?: ^{};
|
||||
}
|
||||
@@ -142,7 +156,7 @@ RCT_EXPORT_MODULE()
|
||||
{
|
||||
id<RCTImageDecoder> imageDecoder = [self imageDecoderForRequest:data];
|
||||
if (imageDecoder) {
|
||||
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, id image) {
|
||||
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, UIImage *image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
}];
|
||||
} else {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
@implementation RCTImageView
|
||||
{
|
||||
RCTBridge *_bridge;
|
||||
CGSize _targetSize;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
@@ -142,10 +143,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
scale:RCTScreenScale()
|
||||
resizeMode:self.contentMode
|
||||
progressBlock:progressHandler
|
||||
completionBlock:^(NSError *error, id image) {
|
||||
completionBlock:^(NSError *error, UIImage *image) {
|
||||
|
||||
if ([image isKindOfClass:[CAAnimation class]]) {
|
||||
[self.layer addAnimation:image forKey:@"contents"];
|
||||
if (image.reactKeyframeAnimation) {
|
||||
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
|
||||
} else {
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.image = image;
|
||||
@@ -173,19 +174,17 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
{
|
||||
[super reactSetFrame:frame];
|
||||
if (self.image == nil) {
|
||||
_targetSize = frame.size;
|
||||
[self reloadImage];
|
||||
} else if ([RCTImageView srcNeedsReload:_src]) {
|
||||
|
||||
// Get optimal image size
|
||||
CGSize currentSize = self.image.size;
|
||||
CGSize idealSize = RCTTargetSize(self.image.size, self.image.scale, frame.size,
|
||||
RCTScreenScale(), self.contentMode, YES);
|
||||
|
||||
CGFloat widthChangeFraction = ABS(currentSize.width - idealSize.width) / currentSize.width;
|
||||
CGFloat heightChangeFraction = ABS(currentSize.height - idealSize.height) / currentSize.height;
|
||||
CGFloat widthChangeFraction = ABS(_targetSize.width - idealSize.width) / _targetSize.width;
|
||||
CGFloat heightChangeFraction = ABS(_targetSize.height - idealSize.height) / _targetSize.height;
|
||||
|
||||
// If the combined change is more than 20%, reload the asset in case there is a better size.
|
||||
if (widthChangeFraction + heightChangeFraction > 0.2) {
|
||||
_targetSize = idealSize;
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class Modal extends React.Component {
|
||||
<RCTModalHostView
|
||||
animated={this.props.animated}
|
||||
transparent={this.props.transparent}
|
||||
onDismiss={this.props.onDismiss}
|
||||
style={styles.modal}>
|
||||
<View style={[styles.container, containerBackgroundColor]}>
|
||||
{this.props.children}
|
||||
@@ -45,6 +46,7 @@ class Modal extends React.Component {
|
||||
Modal.propTypes = {
|
||||
animated: PropTypes.bool,
|
||||
transparent: PropTypes.bool,
|
||||
onDismiss: PropTypes.func,
|
||||
};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
|
||||
@@ -138,6 +138,23 @@ type ConnectivityStateAndroid = $Enum<{
|
||||
|
||||
var _subscriptions = new Map();
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
var _isConnected = function(
|
||||
reachability: ReachabilityStateIOS
|
||||
): bool {
|
||||
return reachability !== 'none' &&
|
||||
reachability !== 'unknown';
|
||||
};
|
||||
} else if (Platform.OS === 'android') {
|
||||
var _isConnected = function(
|
||||
connectionType: ConnectivityStateAndroid
|
||||
): bool {
|
||||
return connectionType !== 'NONE' && connectionType !== 'UNKNOWN';
|
||||
};
|
||||
}
|
||||
|
||||
var _isConnectedSubscriptions = new Map();
|
||||
|
||||
var NetInfo = {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
@@ -175,60 +192,41 @@ var NetInfo = {
|
||||
});
|
||||
},
|
||||
|
||||
isConnected: {},
|
||||
isConnected: {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
var listener = (connection) => {
|
||||
handler(_isConnected(connection));
|
||||
};
|
||||
_isConnectedSubscriptions.set(handler, listener);
|
||||
NetInfo.addEventListener(
|
||||
eventName,
|
||||
listener
|
||||
);
|
||||
},
|
||||
|
||||
isConnectionMetered: {},
|
||||
};
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
var listener = _isConnectedSubscriptions.get(handler);
|
||||
NetInfo.removeEventListener(
|
||||
eventName,
|
||||
listener
|
||||
);
|
||||
_isConnectedSubscriptions.delete(handler);
|
||||
},
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
var _isConnected = function(
|
||||
reachability: ReachabilityStateIOS
|
||||
): bool {
|
||||
return reachability !== 'none' &&
|
||||
reachability !== 'unknown';
|
||||
};
|
||||
} else if (Platform.OS === 'android') {
|
||||
var _isConnected = function(
|
||||
connectionType: ConnectivityStateAndroid
|
||||
): bool {
|
||||
return connectionType !== 'NONE' && connectionType !== 'UNKNOWN';
|
||||
};
|
||||
}
|
||||
|
||||
var _isConnectedSubscriptions = new Map();
|
||||
|
||||
NetInfo.isConnected = {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
var listener = (connection) => {
|
||||
handler(_isConnected(connection));
|
||||
};
|
||||
_isConnectedSubscriptions.set(handler, listener);
|
||||
NetInfo.addEventListener(
|
||||
eventName,
|
||||
listener
|
||||
);
|
||||
fetch: function(): Promise {
|
||||
return NetInfo.fetch().then(
|
||||
(connection) => _isConnected(connection)
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
var listener = _isConnectedSubscriptions.get(handler);
|
||||
NetInfo.removeEventListener(
|
||||
eventName,
|
||||
listener
|
||||
);
|
||||
_isConnectedSubscriptions.delete(handler);
|
||||
},
|
||||
|
||||
fetch: function(): Promise {
|
||||
return NetInfo.fetch().then(
|
||||
(connection) => _isConnected(connection)
|
||||
);
|
||||
},
|
||||
isConnectionMetered: ({}: {} | (callback:Function) => void),
|
||||
};
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
|
||||
@@ -56,6 +56,7 @@ RCT_EXPORT_MODULE()
|
||||
// Lazy setup
|
||||
if (!_session && [self isValid]) {
|
||||
NSOperationQueue *callbackQueue = [NSOperationQueue new];
|
||||
callbackQueue.maxConcurrentOperationCount = 1;
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
_session = [NSURLSession sessionWithConfiguration:configuration
|
||||
delegate:self
|
||||
|
||||
@@ -111,6 +111,12 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
var RCTPickerIOS = requireNativeComponent('RCTPicker', null);
|
||||
var RCTPickerIOS = requireNativeComponent('RCTPicker', PickerIOS, {
|
||||
nativeOnly: {
|
||||
items: true,
|
||||
onChange: true,
|
||||
selectedIndex: true,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = PickerIOS;
|
||||
|
||||
@@ -16,13 +16,8 @@
|
||||
#import "RCTTestModule.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#define TIMEOUT_SECONDS 60
|
||||
|
||||
@interface RCTBridge (RCTTestRunner)
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *batchedBridge;
|
||||
|
||||
@end
|
||||
static const NSTimeInterval kTestTimeoutSeconds = 60;
|
||||
static const NSTimeInterval kTestTeardownTimeoutSeconds = 30;
|
||||
|
||||
@implementation RCTTestRunner
|
||||
{
|
||||
@@ -49,7 +44,7 @@
|
||||
_scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
RCTAssert(_scriptURL != nil, @"Could not locate main.jsBundle");
|
||||
#else
|
||||
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?dev=true&platform=ios", app]];
|
||||
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
@@ -83,52 +78,69 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
- (void)runTest:(SEL)test module:(NSString *)moduleName
|
||||
initialProps:(NSDictionary *)initialProps expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
|
||||
{
|
||||
__block NSString *error = nil;
|
||||
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||
if (level >= RCTLogLevelError) {
|
||||
error = message;
|
||||
__weak id weakJSContext;
|
||||
|
||||
@autoreleasepool {
|
||||
__block NSString *error = nil;
|
||||
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||
if (level >= RCTLogLevelError) {
|
||||
error = message;
|
||||
}
|
||||
});
|
||||
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_scriptURL
|
||||
moduleProvider:_moduleProvider
|
||||
launchOptions:nil];
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
|
||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||
|
||||
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
||||
RCTTestModule *testModule = rootView.bridge.modules[testModuleName];
|
||||
RCTAssert(_testController != nil, @"_testController should not be nil");
|
||||
testModule.controller = _testController;
|
||||
testModule.testSelector = test;
|
||||
testModule.view = rootView;
|
||||
|
||||
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
|
||||
vc.view = [UIView new];
|
||||
[vc.view addSubview:rootView]; // Add as subview so it doesn't get resized
|
||||
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:kTestTimeoutSeconds];
|
||||
while (date.timeIntervalSinceNow > 0 && testModule.status == RCTTestStatusPending && error == nil) {
|
||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
}
|
||||
});
|
||||
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_scriptURL
|
||||
moduleProvider:_moduleProvider
|
||||
launchOptions:nil];
|
||||
// Take a weak reference to the JS context, so we track its deallocation later
|
||||
// (we can only do this now, since it's been lazily initialized)
|
||||
weakJSContext = [[[bridge valueForKey:@"batchedBridge"] valueForKey:@"javaScriptExecutor"] valueForKey:@"context"];
|
||||
[rootView removeFromSuperview];
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
|
||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||
|
||||
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
||||
RCTTestModule *testModule = rootView.bridge.batchedBridge.modules[testModuleName];
|
||||
RCTAssert(_testController != nil, @"_testController should not be nil");
|
||||
testModule.controller = _testController;
|
||||
testModule.testSelector = test;
|
||||
testModule.view = rootView;
|
||||
NSArray *nonLayoutSubviews = [vc.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id subview, NSDictionary *bindings) {
|
||||
return ![NSStringFromClass([subview class]) isEqualToString:@"_UILayoutGuide"];
|
||||
}]];
|
||||
RCTAssert(nonLayoutSubviews.count == 0, @"There shouldn't be any other views: %@", nonLayoutSubviews);
|
||||
|
||||
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
|
||||
vc.view = [UIView new];
|
||||
[vc.view addSubview:rootView]; // Add as subview so it doesn't get resized
|
||||
if (expectErrorBlock) {
|
||||
RCTAssert(expectErrorBlock(error), @"Expected an error but nothing matched.");
|
||||
} else {
|
||||
RCTAssert(error == nil, @"RedBox error: %@", error);
|
||||
RCTAssert(testModule.status != RCTTestStatusPending, @"Test didn't finish within %0.f seconds", kTestTimeoutSeconds);
|
||||
RCTAssert(testModule.status == RCTTestStatusPassed, @"Test failed");
|
||||
}
|
||||
[bridge invalidate];
|
||||
}
|
||||
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
while (date.timeIntervalSinceNow > 0 && testModule.status == RCTTestStatusPending && error == nil) {
|
||||
// Wait for the executor to have shut down completely before returning
|
||||
NSDate *teardownTimeout = [NSDate dateWithTimeIntervalSinceNow:kTestTeardownTimeoutSeconds];
|
||||
while (teardownTimeout.timeIntervalSinceNow > 0 && weakJSContext) {
|
||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
}
|
||||
[rootView removeFromSuperview];
|
||||
|
||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||
|
||||
NSArray *nonLayoutSubviews = [vc.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id subview, NSDictionary *bindings) {
|
||||
return ![NSStringFromClass([subview class]) isEqualToString:@"_UILayoutGuide"];
|
||||
}]];
|
||||
RCTAssert(nonLayoutSubviews.count == 0, @"There shouldn't be any other views: %@", nonLayoutSubviews);
|
||||
|
||||
if (expectErrorBlock) {
|
||||
RCTAssert(expectErrorBlock(error), @"Expected an error but nothing matched.");
|
||||
} else {
|
||||
RCTAssert(error == nil, @"RedBox error: %@", error);
|
||||
RCTAssert(testModule.status != RCTTestStatusPending, @"Test didn't finish within %d seconds", TIMEOUT_SECONDS);
|
||||
RCTAssert(testModule.status == RCTTestStatusPassed, @"Test failed");
|
||||
}
|
||||
RCTAssert(!weakJSContext, @"JS context was not deallocated after being invalidated");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
15
Libraries/StyleSheet/StyleSheetTypes.js
Normal file
15
Libraries/StyleSheet/StyleSheetTypes.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule StyleSheetTypes
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
type Atom = number | bool | Object | Array<?Atom>;
|
||||
export type StyleObj = Atom | Array<?StyleObj>;
|
||||
@@ -14,8 +14,7 @@
|
||||
var StyleSheetRegistry = require('StyleSheetRegistry');
|
||||
var invariant = require('invariant');
|
||||
|
||||
type Atom = number | bool | Object | Array<?Atom>
|
||||
type StyleObj = Atom | Array<?StyleObj>
|
||||
import type { StyleObj } from 'StyleSheetTypes';
|
||||
|
||||
function getStyle(style) {
|
||||
if (typeof style === 'number') {
|
||||
|
||||
Reference in New Issue
Block a user