Skip to content

Commit

Permalink
[NativeAnimated][iOS] Add support for value listener
Browse files Browse the repository at this point in the history
  • Loading branch information
janicduplessis committed Aug 8, 2016
1 parent bf82a8d commit e596fb6
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 30 deletions.
1 change: 0 additions & 1 deletion Examples/UIExplorer/js/NativeAnimationsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,6 @@ exports.examples = [
},
{
title: 'Animated value listener',
platform: 'android',
render: function() {
return (
<ValueListenerExample />
Expand Down
20 changes: 10 additions & 10 deletions Libraries/Animated/src/AnimatedImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
*/
'use strict';

var DeviceEventEmitter = require('RCTDeviceEventEmitter');
var InteractionManager = require('InteractionManager');
var Interpolation = require('Interpolation');
var React = require('React');
Expand Down Expand Up @@ -744,23 +743,24 @@ class AnimatedValue extends AnimatedWithChildren {
}

_startListeningToNativeValueUpdates() {
if (this.__nativeAnimatedValueListener ||
!NativeAnimatedHelper.supportsNativeListener()) {
if (this.__nativeAnimatedValueListener) {
return;
}

NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
this.__nativeAnimatedValueListener = DeviceEventEmitter.addListener('onAnimatedValueUpdate', (data) => {
if (data.tag !== this.__getNativeTag()) {
return;
this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener(
'onAnimatedValueUpdate',
(data) => {
if (data.tag !== this.__getNativeTag()) {
return;
}
this._updateValue(data.value, false /* flush */);
}
this._updateValue(data.value, false /* flush */);
});
);
}

_stopListeningForNativeValueUpdates() {
if (!this.__nativeAnimatedValueListener ||
!NativeAnimatedHelper.supportsNativeListener()) {
if (!this.__nativeAnimatedValueListener) {
return;
}

Expand Down
26 changes: 12 additions & 14 deletions Libraries/Animated/src/NativeAnimatedHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

const nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);

/**
* 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);
Expand Down Expand Up @@ -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,
Expand All @@ -91,7 +94,7 @@ var PROPS_WHITELIST = {
},
};

var TRANSFORM_WHITELIST = {
const TRANSFORM_WHITELIST = {
translateX: true,
translateY: true,
scale: true,
Expand Down Expand Up @@ -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,
Expand All @@ -166,5 +164,5 @@ module.exports = {
generateNewNodeTag,
generateNewAnimationId,
assertNativeAnimatedModule,
supportsNativeListener,
nativeEventEmitter,
};
3 changes: 2 additions & 1 deletion Libraries/Animated/src/__tests__/Animated-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ jest
.setMock('Text', {})
.setMock('View', {})
.setMock('Image', {})
.setMock('React', {Component: class {}});
.setMock('React', {Component: class {}})
.setMock('NativeEventEmitter', class {});

var Animated = require('Animated');

Expand Down
9 changes: 9 additions & 0 deletions Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,13 @@

@implementation RCTValueAnimatedNode

- (void)setValue:(CGFloat)value
{
_value = value;

if (_valueObserver) {
[_valueObserver animatedNode:self didUpdateValue:_value];
}
}

@end
4 changes: 3 additions & 1 deletion Libraries/NativeAnimation/RCTNativeAnimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
34 changes: 31 additions & 3 deletions Libraries/NativeAnimation/RCTNativeAnimatedModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -47,11 +46,17 @@ - (void)setBridge:(RCTBridge *)bridge
_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)
{
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e596fb6

Please sign in to comment.