Add support for value listener

Summary:
Adds support for `Animated.Value#addListener` for native driven animated values. Same as #8844 but for iOS. This depends on some JS code in #8844 so only review the 2nd commit and let's wait for #8844 to land first.

**Test plan**
Tested using the UIExplorer example.
Closes https://github.com/facebook/react-native/pull/9194

Differential Revision: D3681749

fbshipit-source-id: 521a61e2221c1ad1f6f40c75dd2dc957361d0271
This commit is contained in:
Janic Duplessis 2016-08-11 18:10:16 -07:00 committed by Facebook Github Bot 6
parent 68d483e041
commit 0e204e1141
7 changed files with 72 additions and 25 deletions

View File

@ -347,7 +347,6 @@ exports.examples = [
},
{
title: 'Animated value listener',
platform: 'android',
render: function() {
return (
<ValueListenerExample />

View File

@ -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;
}

View File

@ -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;
},
};

View File

@ -10,8 +10,17 @@
#import "RCTAnimatedNode.h"
#import <UIKit/UIKit.h>
@class RCTValueAnimatedNode;
@protocol RCTValueAnimatedNodeObserver <NSObject>
- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value;
@end
@interface RCTValueAnimatedNode : RCTAnimatedNode
@property (nonatomic, assign) CGFloat value;
@property (nonatomic, weak) id<RCTValueAnimatedNodeObserver> valueObserver;
@end

View File

@ -11,4 +11,13 @@
@implementation RCTValueAnimatedNode
- (void)setValue:(CGFloat)value
{
_value = value;
if (_valueObserver) {
[_valueObserver animatedNode:self didUpdateValue:_value];
}
}
@end

View File

@ -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 <RCTBridgeModule>
@interface RCTNativeAnimatedModule : RCTEventEmitter <RCTBridgeModule, RCTValueAnimatedNodeObserver>
@end

View File

@ -32,13 +32,12 @@
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 @@ RCT_EXPORT_MODULE()
_propAnimationNodes = [NSMutableSet new];
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"onAnimatedValueUpdate"];
}
RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
config:(NSDictionary<NSString *, id> *)config)
{
@ -198,6 +203,29 @@ RCT_EXPORT_METHOD(dropAnimatedNode:(nonnull NSNumber *)tag)
}
}
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