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:
parent
68d483e041
commit
0e204e1141
|
@ -347,7 +347,6 @@ exports.examples = [
|
|||
},
|
||||
{
|
||||
title: 'Animated value listener',
|
||||
platform: 'android',
|
||||
render: function() {
|
||||
return (
|
||||
<ValueListenerExample />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,4 +11,13 @@
|
|||
|
||||
@implementation RCTValueAnimatedNode
|
||||
|
||||
- (void)setValue:(CGFloat)value
|
||||
{
|
||||
_value = value;
|
||||
|
||||
if (_valueObserver) {
|
||||
[_valueObserver animatedNode:self didUpdateValue:_value];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue