react-native/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
Pieter De Baets e1577df1fd Move all header imports to "<React/..>"
Summary:
To make React Native play nicely with our internal build infrastructure we need to properly namespace all of our header includes.

Where previously you could do `#import "RCTBridge.h"`, you must now write this as `#import <React/RCTBridge.h>`. If your xcode project still has a custom header include path, both variants will likely continue to work, but for new projects, we're defaulting the header include path to `$(BUILT_PRODUCTS_DIR)/usr/local/include`, where the React and CSSLayout targets will copy a subset of headers too. To make Xcode copy headers phase work properly, you may need to add React as an explicit dependency to your app's scheme and disable "parallelize build".

Reviewed By: mmmulani

Differential Revision: D4213120

fbshipit-source-id: 84a32a4b250c27699e6795f43584f13d594a9a82
2016-11-23 07:58:39 -08:00

407 lines
13 KiB
Objective-C

/**
* 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.
*/
#import "RCTNativeAnimatedModule.h"
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTLog.h>
#import "RCTAdditionAnimatedNode.h"
#import "RCTAnimationDriver.h"
#import "RCTAnimationUtils.h"
#import "RCTDiffClampAnimatedNode.h"
#import "RCTDivisionAnimatedNode.h"
#import "RCTEventAnimation.h"
#import "RCTFrameAnimation.h"
#import "RCTInterpolationAnimatedNode.h"
#import "RCTModuloAnimatedNode.h"
#import "RCTMultiplicationAnimatedNode.h"
#import "RCTPropsAnimatedNode.h"
#import "RCTSpringAnimation.h"
#import "RCTStyleAnimatedNode.h"
#import "RCTTransformAnimatedNode.h"
#import "RCTValueAnimatedNode.h"
@implementation RCTNativeAnimatedModule
{
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes;
NSMutableDictionary<NSNumber *, id<RCTAnimationDriver>> *_animationDrivers;
NSMutableDictionary<NSString *, RCTEventAnimation *> *_eventAnimationDrivers;
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations;
NSMutableSet<id<RCTAnimationDriver>> *_finishedAnimations;
NSMutableSet<RCTValueAnimatedNode *> *_updatedValueNodes;
NSMutableSet<RCTPropsAnimatedNode *> *_propAnimationNodes;
CADisplayLink *_displayLink;
}
RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
_animationNodes = [NSMutableDictionary new];
_animationDrivers = [NSMutableDictionary new];
_eventAnimationDrivers = [NSMutableDictionary new];
_activeAnimations = [NSMutableSet new];
_finishedAnimations = [NSMutableSet new];
_updatedValueNodes = [NSMutableSet new];
_propAnimationNodes = [NSMutableSet new];
[bridge.eventDispatcher addDispatchObserver:self];
}
- (void)dealloc
{
[self.bridge.eventDispatcher removeDispatchObserver:self];
}
- (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)
{
static NSDictionary *map;
static dispatch_once_t mapToken;
dispatch_once(&mapToken, ^{
map = @{@"style" : [RCTStyleAnimatedNode class],
@"value" : [RCTValueAnimatedNode class],
@"props" : [RCTPropsAnimatedNode class],
@"interpolation" : [RCTInterpolationAnimatedNode class],
@"addition" : [RCTAdditionAnimatedNode class],
@"diffclamp": [RCTDiffClampAnimatedNode class],
@"division" : [RCTDivisionAnimatedNode class],
@"multiplication" : [RCTMultiplicationAnimatedNode class],
@"modulus" : [RCTModuloAnimatedNode class],
@"transform" : [RCTTransformAnimatedNode class]};
});
NSString *nodeType = [RCTConvert NSString:config[@"type"]];
Class nodeClass = map[nodeType];
if (!nodeClass) {
RCTLogError(@"Animated node type %@ not supported natively", nodeType);
return;
}
RCTAnimatedNode *node = [[nodeClass alloc] initWithTag:tag config:config];
_animationNodes[tag] = node;
if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[_propAnimationNodes addObject:(RCTPropsAnimatedNode *)node];
}
}
RCT_EXPORT_METHOD(connectAnimatedNodes:(nonnull NSNumber *)parentTag
childTag:(nonnull NSNumber *)childTag)
{
RCTAssertParam(parentTag);
RCTAssertParam(childTag);
RCTAnimatedNode *parentNode = _animationNodes[parentTag];
RCTAnimatedNode *childNode = _animationNodes[childTag];
RCTAssertParam(parentNode);
RCTAssertParam(childNode);
[parentNode addChild:childNode];
}
RCT_EXPORT_METHOD(disconnectAnimatedNodes:(nonnull NSNumber *)parentTag
childTag:(nonnull NSNumber *)childTag)
{
RCTAssertParam(parentTag);
RCTAssertParam(childTag);
RCTAnimatedNode *parentNode = _animationNodes[parentTag];
RCTAnimatedNode *childNode = _animationNodes[childTag];
RCTAssertParam(parentNode);
RCTAssertParam(childNode);
[parentNode removeChild:childNode];
}
RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId
nodeTag:(nonnull NSNumber *)nodeTag
config:(NSDictionary<NSString *, id> *)config
endCallback:(RCTResponseSenderBlock)callBack)
{
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag];
NSString *type = config[@"type"];
id<RCTAnimationDriver> animationDriver;
if ([type isEqual:@"frames"]) {
animationDriver = [[RCTFrameAnimation alloc] initWithId:animationId
config:config
forNode:valueNode
callBack:callBack];
} else if ([type isEqual:@"spring"]) {
animationDriver = [[RCTSpringAnimation alloc] initWithId:animationId
config:config
forNode:valueNode
callBack:callBack];
} else {
RCTLogError(@"Unsupported animation type: %@", config[@"type"]);
return;
}
[_activeAnimations addObject:animationDriver];
_animationDrivers[animationId] = animationDriver;
[animationDriver startAnimation];
[self startAnimationLoopIfNeeded];
}
RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)animationId)
{
id<RCTAnimationDriver> driver = _animationDrivers[animationId];
if (driver) {
[driver removeAnimation];
[_animationDrivers removeObjectForKey:animationId];
[_activeAnimations removeObject:driver];
[_finishedAnimations removeObject:driver];
}
}
RCT_EXPORT_METHOD(setAnimatedNodeValue:(nonnull NSNumber *)nodeTag
value:(nonnull NSNumber *)value)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
valueNode.value = value.floatValue;
[valueNode setNeedsUpdate];
[self updateViewsProps];
[valueNode cleanupAnimationUpdate];
}
RCT_EXPORT_METHOD(setAnimatedNodeOffset:(nonnull NSNumber *)nodeTag
offset:(nonnull NSNumber *)offset)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
[valueNode setOffset:offset.floatValue];
[_updatedValueNodes addObject:valueNode];
}
RCT_EXPORT_METHOD(flattenAnimatedNodeOffset:(nonnull NSNumber *)nodeTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
[valueNode flattenOffset];
}
RCT_EXPORT_METHOD(extractAnimatedNodeOffset:(nonnull NSNumber *)nodeTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
[valueNode extractOffset];
}
RCT_EXPORT_METHOD(connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (viewTag && [node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[(RCTPropsAnimatedNode *)node connectToView:viewTag animatedModule:self];
}
}
RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (viewTag && node && [node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[(RCTPropsAnimatedNode *)node disconnectFromView:viewTag];
}
}
RCT_EXPORT_METHOD(dropAnimatedNode:(nonnull NSNumber *)tag)
{
RCTAnimatedNode *node = _animationNodes[tag];
if (node) {
[node detachNode];
[_animationNodes removeObjectForKey:tag];
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
[_updatedValueNodes removeObject:(RCTValueAnimatedNode *)node];
} else if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[_propAnimationNodes removeObject:(RCTPropsAnimatedNode *)node];
}
}
}
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;
}
}
RCT_EXPORT_METHOD(addAnimatedEventToView:(nonnull NSNumber *)viewTag
eventName:(nonnull NSString *)eventName
eventMapping:(NSDictionary<NSString *, id> *)eventMapping)
{
NSNumber *nodeTag = [RCTConvert NSNumber:eventMapping[@"animatedValueTag"]];
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (!node) {
RCTLogError(@"Animated node with tag %@ does not exists", nodeTag);
return;
}
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Animated node connected to event should be of type RCTValueAnimatedNode");
return;
}
NSArray<NSString *> *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]];
RCTEventAnimation *driver =
[[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node];
_eventAnimationDrivers[[NSString stringWithFormat:@"%@%@", viewTag, eventName]] = driver;
}
RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
eventName:(nonnull NSString *)eventName)
{
[_eventAnimationDrivers removeObjectForKey:[NSString stringWithFormat:@"%@%@", viewTag, eventName]];
}
- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
{
[self sendEventWithName:@"onAnimatedValueUpdate"
body:@{@"tag": node.nodeTag, @"value": @(value)}];
}
- (void)eventDispatcherWillDispatchEvent:(id<RCTEvent>)event
{
// Native animated events only work for events dispatched from the main queue.
if (!RCTIsMainQueue() || _eventAnimationDrivers.count == 0) {
return;
}
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName];
RCTEventAnimation *driver = _eventAnimationDrivers[key];
if (driver) {
[driver updateWithEvent:event];
[self updateViewsProps];
[driver.valueNode cleanupAnimationUpdate];
}
}
- (void)updateViewsProps
{
for (RCTPropsAnimatedNode *propsNode in _propAnimationNodes) {
[propsNode updateNodeIfNecessary];
}
}
#pragma mark -- Animation Loop
- (void)startAnimationLoopIfNeeded
{
if (!_displayLink && (_activeAnimations.count > 0 || _updatedValueNodes.count > 0)) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAnimations)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
- (void)stopAnimationLoopIfNeeded
{
if (_displayLink && _activeAnimations.count == 0 && _updatedValueNodes.count == 0) {
[_displayLink invalidate];
_displayLink = nil;
}
}
- (void)updateAnimations
{
// Step Current active animations
// This also recursively marks children nodes as needing update
for (id<RCTAnimationDriver> animationDriver in _activeAnimations) {
[animationDriver stepAnimation];
}
// Perform node updates for marked nodes.
// At this point all nodes that are in need of an update are properly marked as such.
for (RCTPropsAnimatedNode *propsNode in _propAnimationNodes) {
[propsNode updateNodeIfNecessary];
}
// Cleanup nodes and prepare for next cycle. Remove updated nodes from bucket.
for (id<RCTAnimationDriver> driverNode in _activeAnimations) {
[driverNode cleanupAnimationUpdate];
}
for (RCTValueAnimatedNode *valueNode in _updatedValueNodes) {
[valueNode cleanupAnimationUpdate];
}
[_updatedValueNodes removeAllObjects];
for (id<RCTAnimationDriver> driverNode in _activeAnimations) {
if (driverNode.animationHasFinished) {
[driverNode removeAnimation];
[_finishedAnimations addObject:driverNode];
}
}
for (id<RCTAnimationDriver> driverNode in _finishedAnimations) {
[_activeAnimations removeObject:driverNode];
[_animationDrivers removeObjectForKey:driverNode.animationId];
}
[_finishedAnimations removeAllObjects];
[self stopAnimationLoopIfNeeded];
}
@end