/** * 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 "RCTNativeAnimatedNodesManager.h" typedef void (^AnimatedOperation)(RCTNativeAnimatedNodesManager *nodesManager); @implementation RCTNativeAnimatedModule { RCTNativeAnimatedNodesManager *_nodesManager; NSMutableArray *_operations; } RCT_EXPORT_MODULE(); - (void)invalidate { [_nodesManager stopAnimationLoop]; } - (void)dealloc { [self.bridge.eventDispatcher removeDispatchObserver:self]; } - (dispatch_queue_t)methodQueue { return RCTGetUIManagerQueue(); } - (void)setBridge:(RCTBridge *)bridge { [super setBridge:bridge]; _nodesManager = [[RCTNativeAnimatedNodesManager alloc] initWithUIManager:self.bridge.uiManager]; _operations = [NSMutableArray new]; [bridge.eventDispatcher addDispatchObserver:self]; } #pragma mark -- API RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag config:(NSDictionary *)config) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager createAnimatedNode:tag config:config]; }]; } RCT_EXPORT_METHOD(connectAnimatedNodes:(nonnull NSNumber *)parentTag childTag:(nonnull NSNumber *)childTag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager connectAnimatedNodes:parentTag childTag:childTag]; }]; } RCT_EXPORT_METHOD(disconnectAnimatedNodes:(nonnull NSNumber *)parentTag childTag:(nonnull NSNumber *)childTag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager disconnectAnimatedNodes:parentTag childTag:childTag]; }]; } RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId nodeTag:(nonnull NSNumber *)nodeTag config:(NSDictionary *)config endCallback:(RCTResponseSenderBlock)callBack) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager startAnimatingNode:animationId nodeTag:nodeTag config:config endCallback:callBack]; }]; } RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)animationId) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager stopAnimation:animationId]; }]; } RCT_EXPORT_METHOD(setAnimatedNodeValue:(nonnull NSNumber *)nodeTag value:(nonnull NSNumber *)value) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager setAnimatedNodeValue:nodeTag value:value]; }]; } RCT_EXPORT_METHOD(setAnimatedNodeOffset:(nonnull NSNumber *)nodeTag offset:(nonnull NSNumber *)offset) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager setAnimatedNodeOffset:nodeTag offset:offset]; }]; } RCT_EXPORT_METHOD(flattenAnimatedNodeOffset:(nonnull NSNumber *)nodeTag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager flattenAnimatedNodeOffset:nodeTag]; }]; } RCT_EXPORT_METHOD(extractAnimatedNodeOffset:(nonnull NSNumber *)nodeTag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager extractAnimatedNodeOffset:nodeTag]; }]; } RCT_EXPORT_METHOD(connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag viewTag:(nonnull NSNumber *)viewTag) { NSString *viewName = [self.bridge.uiManager viewNameForReactTag:viewTag]; [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager connectAnimatedNodeToView:nodeTag viewTag:viewTag viewName:viewName]; }]; } RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag viewTag:(nonnull NSNumber *)viewTag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager disconnectAnimatedNodeFromView:nodeTag viewTag:viewTag]; }]; } RCT_EXPORT_METHOD(dropAnimatedNode:(nonnull NSNumber *)tag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager dropAnimatedNode:tag]; }]; } RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag) { __weak id valueObserver = self; [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager startListeningToAnimatedNodeValue:tag valueObserver:valueObserver]; }]; } RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag) { __weak id valueObserver = self; [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager stopListeningToAnimatedNodeValue:tag valueObserver:valueObserver]; }]; } RCT_EXPORT_METHOD(addAnimatedEventToView:(nonnull NSNumber *)viewTag eventName:(nonnull NSString *)eventName eventMapping:(NSDictionary *)eventMapping) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager addAnimatedEventToView:viewTag eventName:eventName eventMapping:eventMapping]; }]; } RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag eventName:(nonnull NSString *)eventName animatedNodeTag:(nonnull NSNumber *)animatedNodeTag) { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { [nodesManager removeAnimatedEventFromView:viewTag eventName:eventName animatedNodeTag:animatedNodeTag]; }]; } #pragma mark -- Batch handling - (void)batchDidComplete { NSArray *operations = _operations; _operations = [NSMutableArray new]; dispatch_async(dispatch_get_main_queue(), ^{ [operations enumerateObjectsUsingBlock:^(AnimatedOperation operation, NSUInteger i, BOOL *stop) { operation(self->_nodesManager); }]; [self->_nodesManager updateAnimations]; }); } #pragma mark -- Events - (NSArray *)supportedEvents { return @[@"onAnimatedValueUpdate"]; } - (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value { [self sendEventWithName:@"onAnimatedValueUpdate" body:@{@"tag": node.nodeTag, @"value": @(value)}]; } - (void)eventDispatcherWillDispatchEvent:(id)event { // Native animated events only work for events dispatched from the main queue. if (!RCTIsMainQueue()) { return; } return [_nodesManager handleAnimatedEvent:event]; } @end