diff --git a/Examples/UIExplorer/js/NativeAnimationsExample.js b/Examples/UIExplorer/js/NativeAnimationsExample.js
index b4c151c53df63e..12f317c72143cc 100644
--- a/Examples/UIExplorer/js/NativeAnimationsExample.js
+++ b/Examples/UIExplorer/js/NativeAnimationsExample.js
@@ -347,7 +347,6 @@ exports.examples = [
},
{
title: 'Animated value listener',
- platform: 'android',
render: function() {
return (
diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js
index 8ad81d2de058f8..f7d8b8e163f85d 100644
--- a/Libraries/Animated/src/AnimatedImplementation.js
+++ b/Libraries/Animated/src/AnimatedImplementation.js
@@ -11,7 +11,6 @@
*/
'use strict';
-var DeviceEventEmitter = require('RCTDeviceEventEmitter');
var InteractionManager = require('InteractionManager');
var Interpolation = require('Interpolation');
var React = require('React');
@@ -750,13 +749,12 @@ class AnimatedValue extends AnimatedWithChildren {
}
_startListeningToNativeValueUpdates() {
- if (this.__nativeAnimatedValueListener ||
- !NativeAnimatedHelper.supportsNativeListener()) {
+ if (this.__nativeAnimatedValueListener) {
return;
}
NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
- this.__nativeAnimatedValueListener = DeviceEventEmitter.addListener(
+ this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener(
'onAnimatedValueUpdate',
(data) => {
if (data.tag !== this.__getNativeTag()) {
@@ -768,8 +766,7 @@ class AnimatedValue extends AnimatedWithChildren {
}
_stopListeningForNativeValueUpdates() {
- if (!this.__nativeAnimatedValueListener ||
- !NativeAnimatedHelper.supportsNativeListener()) {
+ if (!this.__nativeAnimatedValueListener) {
return;
}
diff --git a/Libraries/Animated/src/NativeAnimatedHelper.js b/Libraries/Animated/src/NativeAnimatedHelper.js
index 9138a4308e351b..668a1e079ab9f7 100644
--- a/Libraries/Animated/src/NativeAnimatedHelper.js
+++ b/Libraries/Animated/src/NativeAnimatedHelper.js
@@ -11,21 +11,24 @@
*/
'use strict';
-var NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
+const NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
+const NativeEventEmitter = require('NativeEventEmitter');
-var invariant = require('fbjs/lib/invariant');
+const invariant = require('fbjs/lib/invariant');
-var __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
-var __nativeAnimationIdCount = 1; /* used for started animations */
+let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
+let __nativeAnimationIdCount = 1; /* used for started animations */
type EndResult = {finished: bool};
type EndCallback = (result: EndResult) => void;
+let nativeEventEmitter;
+
/**
- * Simple wrappers around NativeANimatedModule to provide flow and autocmplete support for
+ * Simple wrappers around NativeAnimatedModule to provide flow and autocmplete support for
* the native module methods
*/
-var API = {
+const API = {
createAnimatedNode: function(tag: number, config: Object): void {
assertNativeAnimatedModule();
NativeAnimatedModule.createAnimatedNode(tag, config);
@@ -79,7 +82,7 @@ var API = {
* to be updated through the shadow view hierarchy (all non-layout properties). This list is limited
* to the properties that will perform best when animated off the JS thread.
*/
-var PROPS_WHITELIST = {
+const PROPS_WHITELIST = {
style: {
opacity: true,
transform: true,
@@ -91,7 +94,7 @@ var PROPS_WHITELIST = {
},
};
-var TRANSFORM_WHITELIST = {
+const TRANSFORM_WHITELIST = {
translateX: true,
translateY: true,
scale: true,
@@ -152,11 +155,6 @@ function assertNativeAnimatedModule(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
}
-// TODO: remove this when iOS supports native listeners.
-function supportsNativeListener(): bool {
- return !!NativeAnimatedModule.startListeningToAnimatedNodeValue;
-}
-
module.exports = {
API,
validateProps,
@@ -166,5 +164,10 @@ module.exports = {
generateNewNodeTag,
generateNewAnimationId,
assertNativeAnimatedModule,
- supportsNativeListener,
+ get nativeEventEmitter() {
+ if (!nativeEventEmitter) {
+ nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);
+ }
+ return nativeEventEmitter;
+ },
};
diff --git a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
index af183243534cdd..5029129ec27db8 100644
--- a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
+++ b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
@@ -10,8 +10,17 @@
#import "RCTAnimatedNode.h"
#import
+@class RCTValueAnimatedNode;
+
+@protocol RCTValueAnimatedNodeObserver
+
+- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value;
+
+@end
+
@interface RCTValueAnimatedNode : RCTAnimatedNode
@property (nonatomic, assign) CGFloat value;
+@property (nonatomic, weak) id valueObserver;
@end
diff --git a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
index cffa66681d7b17..40ca5b9eafdabd 100644
--- a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
+++ b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
@@ -11,4 +11,13 @@
@implementation RCTValueAnimatedNode
+- (void)setValue:(CGFloat)value
+{
+ _value = value;
+
+ if (_valueObserver) {
+ [_valueObserver animatedNode:self didUpdateValue:_value];
+ }
+}
+
@end
diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h
index d099ac53bfacf9..da7831769194f6 100644
--- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h
+++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h
@@ -7,7 +7,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTBridgeModule.h"
+#import "RCTValueAnimatedNode.h"
+#import "RCTEventEmitter.h"
-@interface RCTNativeAnimatedModule : NSObject
+@interface RCTNativeAnimatedModule : RCTEventEmitter
@end
diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
index 5ba4f322fb3dfa..80c263f7e42a28 100644
--- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
+++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
@@ -32,13 +32,12 @@ @implementation RCTNativeAnimatedModule
CADisplayLink *_displayLink;
}
-@synthesize bridge = _bridge;
-
RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
- _bridge = bridge;
+ [super setBridge:bridge];
+
_animationNodes = [NSMutableDictionary new];
_animationDrivers = [NSMutableDictionary new];
_activeAnimations = [NSMutableSet new];
@@ -47,11 +46,17 @@ - (void)setBridge:(RCTBridge *)bridge
_propAnimationNodes = [NSMutableSet new];
}
+
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
+- (NSArray *)supportedEvents
+{
+ return @[@"onAnimatedValueUpdate"];
+}
+
RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
config:(NSDictionary *)config)
{
@@ -198,6 +203,29 @@ - (dispatch_queue_t)methodQueue
}
}
+RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
+{
+ RCTAnimatedNode *node = _animationNodes[tag];
+ if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) {
+ ((RCTValueAnimatedNode *)node).valueObserver = self;
+ }
+}
+
+RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
+{
+ RCTAnimatedNode *node = _animationNodes[tag];
+ if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) {
+ ((RCTValueAnimatedNode *)node).valueObserver = nil;
+ }
+}
+
+- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
+{
+ [self sendEventWithName:@"onAnimatedValueUpdate"
+ body:@{@"tag": node.nodeTag, @"value": @(value)}];
+}
+
+
#pragma mark -- Animation Loop
- (void)startAnimation