diff --git a/Libraries/NativeAnimation/Drivers/RCTAnimationDriver.h b/Libraries/NativeAnimation/Drivers/RCTAnimationDriver.h index 90fdf78c1422c4..ed2f75f4bc0ab5 100644 --- a/Libraries/NativeAnimation/Drivers/RCTAnimationDriver.h +++ b/Libraries/NativeAnimation/Drivers/RCTAnimationDriver.h @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import #import #import @@ -31,8 +32,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)stopAnimation; - (void)stepAnimation; - (void)removeAnimation; -- (void)cleanupAnimationUpdate; - -@end NS_ASSUME_NONNULL_END + +@end diff --git a/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m b/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m index 40fb4137a4cc27..ebe4d60d7e8d8e 100644 --- a/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m +++ b/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m @@ -150,9 +150,4 @@ - (void)updateOutputWithFrameOutput:(CGFloat)frameOutput [_valueNode setNeedsUpdate]; } -- (void)cleanupAnimationUpdate -{ - [_valueNode cleanupAnimationUpdate]; -} - @end diff --git a/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m b/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m index 94751c871560f1..e0bb3363a34559 100644 --- a/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m +++ b/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m @@ -193,9 +193,4 @@ - (void)onUpdate:(CGFloat)outputValue [_valueNode setNeedsUpdate]; } -- (void)cleanupAnimationUpdate -{ - [_valueNode cleanupAnimationUpdate]; -} - @end diff --git a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h index 8e06e90629fdd6..3632a77a47e75c 100644 --- a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h +++ b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h @@ -21,7 +21,6 @@ @property (nonatomic, copy, readonly) NSDictionary *parentNodes; @property (nonatomic, readonly) BOOL needsUpdate; -@property (nonatomic, readonly) BOOL hasUpdated; /** * Marks a node and its children as needing update. @@ -38,11 +37,6 @@ */ - (void)performUpdate NS_REQUIRES_SUPER; -/** - * Cleans up after a round of updates. - */ -- (void)cleanupAnimationUpdate NS_REQUIRES_SUPER; - - (void)addChild:(RCTAnimatedNode *)child NS_REQUIRES_SUPER; - (void)removeChild:(RCTAnimatedNode *)child NS_REQUIRES_SUPER; diff --git a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m index 1810109e0e539d..be9c843bf7a8d0 100644 --- a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m @@ -93,30 +93,15 @@ - (void)detachNode - (void)setNeedsUpdate { - if (_needsUpdate) { - // Has already been marked. Stop branch. - return; - } _needsUpdate = YES; for (RCTAnimatedNode *child in _childNodes.allValues) { [child setNeedsUpdate]; } } -- (void)cleanupAnimationUpdate -{ - if (_hasUpdated) { - _needsUpdate = NO; - _hasUpdated = NO; - for (RCTAnimatedNode *child in _childNodes.allValues) { - [child cleanupAnimationUpdate]; - } - } -} - - (void)updateNodeIfNecessary { - if (_needsUpdate && !_hasUpdated) { + if (_needsUpdate) { for (RCTAnimatedNode *parent in _parentNodes.allValues) { [parent updateNodeIfNecessary]; } @@ -126,7 +111,7 @@ - (void)updateNodeIfNecessary - (void)performUpdate { - _hasUpdated = YES; + _needsUpdate = NO; // To be overidden by subclasses // This method is called on a node only if it has been marked for update // during the current update loop diff --git a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h index 08debc6c794b74..5613f61407ac8d 100644 --- a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h +++ b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h @@ -9,14 +9,14 @@ #import "RCTAnimatedNode.h" -@class RCTNativeAnimatedModule; +@class RCTUIManager; @class RCTViewPropertyMapper; @interface RCTPropsAnimatedNode : RCTAnimatedNode @property (nonatomic, readonly) RCTViewPropertyMapper *propertyMapper; -- (void)connectToView:(NSNumber *)viewTag animatedModule:(RCTNativeAnimatedModule *)animationModule; +- (void)connectToView:(NSNumber *)viewTag uiManager:(RCTUIManager *)uiManager; - (void)disconnectFromView:(NSNumber *)viewTag; - (void)performViewUpdatesIfNecessary; diff --git a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m index fcd5c34d6643b4..a2017405b1c61e 100644 --- a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m @@ -8,35 +8,17 @@ */ #import "RCTPropsAnimatedNode.h" + #import "RCTAnimationUtils.h" -#import "RCTNativeAnimatedModule.h" #import "RCTStyleAnimatedNode.h" +#import "RCTValueAnimatedNode.h" #import "RCTViewPropertyMapper.h" @implementation RCTPropsAnimatedNode -{ - RCTStyleAnimatedNode *_parentNode; -} - -- (void)onAttachedToNode:(RCTAnimatedNode *)parent -{ - [super onAttachedToNode:parent]; - if ([parent isKindOfClass:[RCTStyleAnimatedNode class]]) { - _parentNode = (RCTStyleAnimatedNode *)parent; - } -} - -- (void)onDetachedFromNode:(RCTAnimatedNode *)parent -{ - [super onDetachedFromNode:parent]; - if (_parentNode == parent) { - _parentNode = nil; - } -} -- (void)connectToView:(NSNumber *)viewTag animatedModule:(RCTNativeAnimatedModule *)animationModule +- (void)connectToView:(NSNumber *)viewTag uiManager:(RCTUIManager *)uiManager { - _propertyMapper = [[RCTViewPropertyMapper alloc] initWithViewTag:viewTag animationModule:animationModule]; + _propertyMapper = [[RCTViewPropertyMapper alloc] initWithViewTag:viewTag uiManager:uiManager]; } - (void)disconnectFromView:(NSNumber *)viewTag @@ -50,11 +32,36 @@ - (void)performUpdate [self performViewUpdatesIfNecessary]; } +- (NSString *)propertyNameForParentTag:(NSNumber *)parentTag +{ + __block NSString *propertyName; + [self.config[@"props"] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull property, NSNumber * _Nonnull tag, BOOL * _Nonnull stop) { + if ([tag isEqualToNumber:parentTag]) { + propertyName = property; + *stop = YES; + } + }]; + return propertyName; +} + - (void)performViewUpdatesIfNecessary { - NSDictionary *updates = [_parentNode updatedPropsDictionary]; - if (updates.count) { - [_propertyMapper updateViewWithDictionary:updates]; + NSMutableDictionary *props = [NSMutableDictionary dictionary]; + [self.parentNodes enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull parentTag, RCTAnimatedNode * _Nonnull parentNode, BOOL * _Nonnull stop) { + + if ([parentNode isKindOfClass:[RCTStyleAnimatedNode class]]) { + [props addEntriesFromDictionary:[(RCTStyleAnimatedNode *)parentNode propsDictionary]]; + + } else if ([parentNode isKindOfClass:[RCTValueAnimatedNode class]]) { + NSString *property = [self propertyNameForParentTag:parentTag]; + CGFloat value = [(RCTValueAnimatedNode *)parentNode value]; + [props setObject:@(value) forKey:property]; + } + + }]; + + if (props.count) { + [_propertyMapper updateViewWithDictionary:props]; } } diff --git a/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.h index 9a3dbbe32dd470..285ebf9deea6a0 100644 --- a/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.h +++ b/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.h @@ -11,6 +11,6 @@ @interface RCTStyleAnimatedNode : RCTAnimatedNode -- (NSDictionary *)updatedPropsDictionary; +- (NSDictionary *)propsDictionary; @end diff --git a/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m index 2c349bd22e81d7..e3bb4d8436c6cd 100644 --- a/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m @@ -14,21 +14,21 @@ @implementation RCTStyleAnimatedNode { - NSMutableDictionary *_updatedPropsDictionary; + NSMutableDictionary *_propsDictionary; } - (instancetype)initWithTag:(NSNumber *)tag config:(NSDictionary *)config; { if ((self = [super initWithTag:tag config:config])) { - _updatedPropsDictionary = [NSMutableDictionary new]; + _propsDictionary = [NSMutableDictionary new]; } return self; } -- (NSDictionary *)updatedPropsDictionary +- (NSDictionary *)propsDictionary { - return _updatedPropsDictionary; + return _propsDictionary; } - (void)performUpdate @@ -38,22 +38,16 @@ - (void)performUpdate NSDictionary *style = self.config[@"style"]; [style enumerateKeysAndObjectsUsingBlock:^(NSString *property, NSNumber *nodeTag, __unused BOOL *stop) { RCTAnimatedNode *node = self.parentNodes[nodeTag]; - if (node && node.hasUpdated) { + if (node) { if ([node isKindOfClass:[RCTValueAnimatedNode class]]) { RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node; - [self->_updatedPropsDictionary setObject:@(parentNode.value) forKey:property]; + [self->_propsDictionary setObject:@(parentNode.value) forKey:property]; } else if ([node isKindOfClass:[RCTTransformAnimatedNode class]]) { RCTTransformAnimatedNode *parentNode = (RCTTransformAnimatedNode *)node; - [self->_updatedPropsDictionary addEntriesFromDictionary:parentNode.updatedPropsDictionary]; + [self->_propsDictionary addEntriesFromDictionary:parentNode.propsDictionary]; } } }]; } -- (void)cleanupAnimationUpdate -{ - [super cleanupAnimationUpdate]; - [_updatedPropsDictionary removeAllObjects]; -} - @end diff --git a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.h index 41366172d841a3..18d9eba0b8aa76 100644 --- a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.h +++ b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.h @@ -11,6 +11,6 @@ @interface RCTTransformAnimatedNode : RCTAnimatedNode -- (NSDictionary *)updatedPropsDictionary; +- (NSDictionary *)propsDictionary; @end diff --git a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m index 9c60d2c0ab3070..07a89344b5d6df 100644 --- a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m @@ -12,21 +12,21 @@ @implementation RCTTransformAnimatedNode { - NSMutableDictionary *_updatedPropsDictionary; + NSMutableDictionary *_propsDictionary; } - (instancetype)initWithTag:(NSNumber *)tag config:(NSDictionary *)config; { if ((self = [super initWithTag:tag config:config])) { - _updatedPropsDictionary = [NSMutableDictionary new]; + _propsDictionary = [NSMutableDictionary new]; } return self; } -- (NSDictionary *)updatedPropsDictionary +- (NSDictionary *)propsDictionary { - return _updatedPropsDictionary; + return _propsDictionary; } - (void)performUpdate @@ -44,7 +44,7 @@ - (void)performUpdate if ([type isEqualToString: @"animated"]) { NSNumber *nodeTag = transformConfig[@"nodeTag"]; RCTAnimatedNode *node = self.parentNodes[nodeTag]; - if (!node.hasUpdated || ![node isKindOfClass:[RCTValueAnimatedNode class]]) { + if (![node isKindOfClass:[RCTValueAnimatedNode class]]) { continue; } RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node; @@ -82,13 +82,7 @@ - (void)performUpdate } } - _updatedPropsDictionary[@"transform"] = [NSValue valueWithCATransform3D:transform]; -} - -- (void)cleanupAnimationUpdate -{ - [super cleanupAnimationUpdate]; - [_updatedPropsDictionary removeAllObjects]; + _propsDictionary[@"transform"] = [NSValue valueWithCATransform3D:transform]; } @end diff --git a/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj b/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj index 3a2608e7da7c40..9dff5d8e5d4369 100644 --- a/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj +++ b/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj @@ -38,8 +38,10 @@ 5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; }; 944244D01DB962DA0032A02B /* RCTFrameAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */; }; 944244D11DB962DC0032A02B /* RCTSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */; }; + 9476E8EC1DC9232D005D5CD1 /* RCTNativeAnimatedNodesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DA09171DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m */; }; 94C129511D40692B0025F25C /* RCTFrameAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */; }; 94C129521D40692B0025F25C /* RCTSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */; }; + 94DA09181DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DA09171DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m */; }; 94DAE3F91D7334A70059942F /* RCTModuloAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */; }; /* End PBXBuildFile section */ @@ -79,6 +81,8 @@ 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTFrameAnimation.m; sourceTree = ""; }; 94C1294E1D4069170025F25C /* RCTSpringAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTSpringAnimation.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTSpringAnimation.m; sourceTree = ""; }; + 94DA09161DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNativeAnimatedNodesManager.h; sourceTree = ""; }; + 94DA09171DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNativeAnimatedNodesManager.m; sourceTree = ""; }; 94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTModuloAnimatedNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuloAnimatedNode.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -130,6 +134,8 @@ 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */, 13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */, 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */, + 94DA09161DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.h */, + 94DA09171DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m */, 94C129491D4069170025F25C /* Drivers */, 13E501D51D07A6C9005F35D8 /* Nodes */, 134814211AA4EA7D00B7C361 /* Products */, @@ -242,6 +248,7 @@ 2D3B5EF31D9B0B3400451313 /* RCTViewPropertyMapper.m in Sources */, 944244D01DB962DA0032A02B /* RCTFrameAnimation.m in Sources */, 944244D11DB962DC0032A02B /* RCTSpringAnimation.m in Sources */, + 9476E8EC1DC9232D005D5CD1 /* RCTNativeAnimatedNodesManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -251,6 +258,7 @@ files = ( 94C129511D40692B0025F25C /* RCTFrameAnimation.m in Sources */, 94C129521D40692B0025F25C /* RCTSpringAnimation.m in Sources */, + 94DA09181DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m in Sources */, 13E501F01D07A6C9005F35D8 /* RCTValueAnimatedNode.m in Sources */, 94DAE3F91D7334A70059942F /* RCTModuloAnimatedNode.m in Sources */, 193F64F41D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m in Sources */, diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h index 48b0214814bee0..4bc7831b1b9ade 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h @@ -6,6 +6,7 @@ * 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 #import #import diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m index 86027d3b42b7d1..5f7a17303f3608 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m @@ -8,134 +8,62 @@ */ #import "RCTNativeAnimatedModule.h" -#import -#import -#import - -#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" +#import "RCTNativeAnimatedNodesManager.h" + +typedef void (^AnimatedOperation)(RCTNativeAnimatedNodesManager *nodesManager); @implementation RCTNativeAnimatedModule { - NSMutableDictionary *_animationNodes; - NSMutableDictionary> *_animationDrivers; - NSMutableDictionary *_eventAnimationDrivers; - NSMutableSet> *_activeAnimations; - NSMutableSet> *_finishedAnimations; - NSMutableSet *_updatedValueNodes; - NSMutableSet *_propAnimationNodes; - CADisplayLink *_displayLink; + RCTNativeAnimatedNodesManager *_nodesManager; + NSMutableArray *_operations; } -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]; -} +RCT_EXPORT_MODULE(); - (void)dealloc { [self.bridge.eventDispatcher removeDispatchObserver:self]; } - - (dispatch_queue_t)methodQueue { - return dispatch_get_main_queue(); + return RCTGetUIManagerQueue(); } -- (NSArray *)supportedEvents +- (void)setBridge:(RCTBridge *)bridge { - return @[@"onAnimatedValueUpdate"]; + [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) { - 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]; - } + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager createAnimatedNode:tag config:config]; + }]; } 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager connectAnimatedNodes:parentTag childTag:childTag]; + }]; } 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager disconnectAnimatedNodes:parentTag childTag:childTag]; + }]; } RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId @@ -143,264 +71,139 @@ - (dispatch_queue_t)methodQueue config:(NSDictionary *)config endCallback:(RCTResponseSenderBlock)callBack) { - RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag]; - - NSString *type = config[@"type"]; - id 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager startAnimatingNode:animationId nodeTag:nodeTag config:config endCallback:callBack]; + }]; } RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)animationId) { - id driver = _animationDrivers[animationId]; - if (driver) { - [driver removeAnimation]; - [_animationDrivers removeObjectForKey:animationId]; - [_activeAnimations removeObject:driver]; - [_finishedAnimations removeObject:driver]; - } + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager stopAnimation:animationId]; + }]; } 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager setAnimatedNodeValue:nodeTag value:value]; + }]; } 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager setAnimatedNodeOffset:nodeTag offset:offset]; + }]; } 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager flattenAnimatedNodeOffset:nodeTag]; + }]; } 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]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager extractAnimatedNodeOffset:nodeTag]; + }]; } 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]; - } + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager connectAnimatedNodeToView:nodeTag viewTag:viewTag]; + }]; } 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]; - } + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager disconnectAnimatedNodeFromView:nodeTag viewTag: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]; - } - } + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager dropAnimatedNode:tag]; + }]; } RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag) { - RCTAnimatedNode *node = _animationNodes[tag]; - if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) { - ((RCTValueAnimatedNode *)node).valueObserver = self; - } + __weak id valueObserver = self; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager startListeningToAnimatedNodeValue:tag valueObserver:valueObserver]; + }]; } RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag) { - RCTAnimatedNode *node = _animationNodes[tag]; - if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) { - ((RCTValueAnimatedNode *)node).valueObserver = nil; - } + __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) { - 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 *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]]; - - RCTEventAnimation *driver = - [[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node]; - - _eventAnimationDrivers[[NSString stringWithFormat:@"%@%@", viewTag, eventName]] = driver; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager addAnimatedEventToView:viewTag eventName:eventName eventMapping:eventMapping]; + }]; } RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag eventName:(nonnull NSString *)eventName) { - [_eventAnimationDrivers removeObjectForKey:[NSString stringWithFormat:@"%@%@", viewTag, eventName]]; + [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager removeAnimatedEventFromView:viewTag eventName:eventName]; + }]; } -- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value -{ - [self sendEventWithName:@"onAnimatedValueUpdate" - body:@{@"tag": node.nodeTag, @"value": @(value)}]; -} +#pragma mark -- Batch handling -- (void)eventDispatcherWillDispatchEvent:(id)event +- (void)batchDidComplete { - // Native animated events only work for events dispatched from the main queue. - if (!RCTIsMainQueue() || _eventAnimationDrivers.count == 0) { - return; - } + NSArray *operations = _operations; + _operations = [NSMutableArray new]; - 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]; - } + dispatch_async(dispatch_get_main_queue(), ^{ + [operations enumerateObjectsUsingBlock:^(AnimatedOperation operation, NSUInteger i, BOOL *stop) { + operation(self->_nodesManager); + }]; + [self->_nodesManager updateAnimations]; + }); } -#pragma mark -- Animation Loop +#pragma mark -- Events -- (void)startAnimationLoopIfNeeded +- (NSArray *)supportedEvents { - if (!_displayLink && (_activeAnimations.count > 0 || _updatedValueNodes.count > 0)) { - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAnimations)]; - [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; - } + return @[@"onAnimatedValueUpdate"]; } -- (void)stopAnimationLoopIfNeeded +- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value { - if (_displayLink && _activeAnimations.count == 0 && _updatedValueNodes.count == 0) { - [_displayLink invalidate]; - _displayLink = nil; - } + [self sendEventWithName:@"onAnimatedValueUpdate" + body:@{@"tag": node.nodeTag, @"value": @(value)}]; } -- (void)updateAnimations +- (void)eventDispatcherWillDispatchEvent:(id)event { - // Step Current active animations - // This also recursively marks children nodes as needing update - for (id 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 driverNode in _activeAnimations) { - [driverNode cleanupAnimationUpdate]; - } - for (RCTValueAnimatedNode *valueNode in _updatedValueNodes) { - [valueNode cleanupAnimationUpdate]; - } - [_updatedValueNodes removeAllObjects]; - - for (id driverNode in _activeAnimations) { - if (driverNode.animationHasFinished) { - [driverNode removeAnimation]; - [_finishedAnimations addObject:driverNode]; - } - } - for (id driverNode in _finishedAnimations) { - [_activeAnimations removeObject:driverNode]; - [_animationDrivers removeObjectForKey:driverNode.animationId]; + // Native animated events only work for events dispatched from the main queue. + if (!RCTIsMainQueue()) { + return; } - [_finishedAnimations removeAllObjects]; - - [self stopAnimationLoopIfNeeded]; + return [_nodesManager handleAnimatedEvent:event]; } @end diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h new file mode 100644 index 00000000000000..25c4bcb8f26d1e --- /dev/null +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -0,0 +1,82 @@ +/** + * 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 + +#import +#import + +#import "RCTValueAnimatedNode.h" + +@interface RCTNativeAnimatedNodesManager : NSObject + +- (nonnull instancetype)initWithUIManager:(nonnull RCTUIManager *)uiManager; + +- (void)updateAnimations; + +// graph + +- (void)createAnimatedNode:(nonnull NSNumber *)tag + config:(NSDictionary *__nonnull)config; + +- (void)connectAnimatedNodes:(nonnull NSNumber *)parentTag + childTag:(nonnull NSNumber *)childTag; + +- (void)disconnectAnimatedNodes:(nonnull NSNumber *)parentTag + childTag:(nonnull NSNumber *)childTag; + +- (void)connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag + viewTag:(nonnull NSNumber *)viewTag; + +- (void)disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag + viewTag:(nonnull NSNumber *)viewTag; + +- (void)dropAnimatedNode:(nonnull NSNumber *)tag; + +// mutations + +- (void)setAnimatedNodeValue:(nonnull NSNumber *)nodeTag + value:(nonnull NSNumber *)value; + +- (void)setAnimatedNodeOffset:(nonnull NSNumber *)nodeTag + offset:(nonnull NSNumber *)offset; + +- (void)flattenAnimatedNodeOffset:(nonnull NSNumber *)nodeTag; + +- (void)extractAnimatedNodeOffset:(nonnull NSNumber *)nodeTag; + +// drivers + +- (void)startAnimatingNode:(nonnull NSNumber *)animationId + nodeTag:(nonnull NSNumber *)nodeTag + config:(NSDictionary *__nonnull)config + endCallback:(nullable RCTResponseSenderBlock)callBack; + +- (void)stopAnimation:(nonnull NSNumber *)animationId; + +// events + +- (void)addAnimatedEventToView:(nonnull NSNumber *)viewTag + eventName:(nonnull NSString *)eventName + eventMapping:(NSDictionary *__nonnull)eventMapping; + +- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag + eventName:(nonnull NSString *)eventName; + +- (void)handleAnimatedEvent:(nonnull id)event; + +// listeners + +- (void)startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag + valueObserver:(nonnull id)valueObserver; + +- (void)stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag + valueObserver:(nonnull id)valueObserver; + +@end diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m new file mode 100644 index 00000000000000..dc6e8337327fb3 --- /dev/null +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m @@ -0,0 +1,360 @@ +/** + * 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 "RCTNativeAnimatedNodesManager.h" + +#import + +#import "RCTAnimatedNode.h" +#import "RCTAnimationDriver.h" +#import "RCTEventAnimation.h" + +#import "RCTAdditionAnimatedNode.h" +#import "RCTInterpolationAnimatedNode.h" +#import "RCTDiffClampAnimatedNode.h" +#import "RCTDivisionAnimatedNode.h" +#import "RCTModuloAnimatedNode.h" +#import "RCTMultiplicationAnimatedNode.h" +#import "RCTModuloAnimatedNode.h" +#import "RCTPropsAnimatedNode.h" +#import "RCTStyleAnimatedNode.h" +#import "RCTTransformAnimatedNode.h" +#import "RCTValueAnimatedNode.h" +#import "RCTFrameAnimation.h" +#import "RCTSpringAnimation.h" + +@implementation RCTNativeAnimatedNodesManager +{ + RCTUIManager *_uiManager; + NSMutableDictionary *_animationNodes; + NSMutableDictionary *_eventDrivers; + NSMutableSet> *_activeAnimations; + CADisplayLink *_displayLink; +} + +- (instancetype)initWithUIManager:(nonnull RCTUIManager *)uiManager +{ + if ((self = [super init])) { + _uiManager = uiManager; + _animationNodes = [NSMutableDictionary new]; + _eventDrivers = [NSMutableDictionary new]; + _activeAnimations = [NSMutableSet new]; + } + return self; +} + +#pragma mark -- Graph + +- (void)createAnimatedNode:(nonnull NSNumber *)tag + config:(NSDictionary *)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; + [node setNeedsUpdate]; +} + +- (void)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]; + [childNode setNeedsUpdate]; +} + +- (void)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]; + [childNode setNeedsUpdate]; +} + +- (void)connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag + viewTag:(nonnull NSNumber *)viewTag +{ + RCTAnimatedNode *node = _animationNodes[nodeTag]; + if (viewTag && [node isKindOfClass:[RCTPropsAnimatedNode class]]) { + [(RCTPropsAnimatedNode *)node connectToView:viewTag uiManager:_uiManager]; + } + [node setNeedsUpdate]; +} + +- (void)disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag + viewTag:(nonnull NSNumber *)viewTag +{ + RCTAnimatedNode *node = _animationNodes[nodeTag]; + if (viewTag && node && [node isKindOfClass:[RCTPropsAnimatedNode class]]) { + [(RCTPropsAnimatedNode *)node disconnectFromView:viewTag]; + } +} + +- (void)dropAnimatedNode:(nonnull NSNumber *)tag +{ + RCTAnimatedNode *node = _animationNodes[tag]; + if (node) { + [node detachNode]; + [_animationNodes removeObjectForKey:tag]; + } +} + +#pragma mark -- Mutations + +- (void)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]; +} + +- (void)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]; + [valueNode setNeedsUpdate]; +} + +- (void)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]; +} + +- (void)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]; +} + +#pragma mark -- Drivers + +- (void)startAnimatingNode:(nonnull NSNumber *)animationId + nodeTag:(nonnull NSNumber *)nodeTag + config:(NSDictionary *)config + endCallback:(RCTResponseSenderBlock)callBack +{ + RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag]; + + NSString *type = config[@"type"]; + idanimationDriver; + + 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]; + [animationDriver startAnimation]; + [self startAnimationLoopIfNeeded]; +} + +- (void)stopAnimation:(nonnull NSNumber *)animationId +{ + for (iddriver in _activeAnimations) { + if ([driver.animationId isEqual:animationId]) { + [driver removeAnimation]; + [_activeAnimations removeObject:driver]; + break; + } + } +} + +#pragma mark -- Events + +- (void)addAnimatedEventToView:(nonnull NSNumber *)viewTag + eventName:(nonnull NSString *)eventName + eventMapping:(NSDictionary *)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 *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]]; + + RCTEventAnimation *driver = + [[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node]; + + _eventDrivers[[NSString stringWithFormat:@"%@%@", viewTag, eventName]] = driver; +} + +- (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag + eventName:(nonnull NSString *)eventName +{ + [_eventDrivers removeObjectForKey:[NSString stringWithFormat:@"%@%@", viewTag, eventName]]; +} + +- (void)handleAnimatedEvent:(id)event +{ + if (_eventDrivers.count == 0) { + return; + } + + NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName]; + RCTEventAnimation *driver = _eventDrivers[key]; + if (driver) { + [driver updateWithEvent:event]; + [self updateAnimations]; + } +} + +#pragma mark -- Listeners + +- (void)startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag + valueObserver:(id)valueObserver +{ + RCTAnimatedNode *node = _animationNodes[tag]; + if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) { + ((RCTValueAnimatedNode *)node).valueObserver = valueObserver; + } +} + +- (void)stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag + valueObserver:(id)valueObserver +{ + RCTAnimatedNode *node = _animationNodes[tag]; + if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) { + ((RCTValueAnimatedNode *)node).valueObserver = valueObserver; + } +} + + +#pragma mark -- Animation Loop + +- (void)startAnimationLoopIfNeeded +{ + if (!_displayLink && _activeAnimations.count > 0) { + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(stepAnimations)]; + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + } +} + +- (void)stopAnimationLoopIfNeeded +{ + if (_displayLink && _activeAnimations.count == 0) { + [_displayLink invalidate]; + _displayLink = nil; + } +} + +- (void)stepAnimations +{ + for (idanimationDriver in _activeAnimations) { + [animationDriver stepAnimation]; + } + + [self updateAnimations]; + + for (idanimationDriver in [_activeAnimations copy]) { + if (animationDriver.animationHasFinished) { + [animationDriver removeAnimation]; + [_activeAnimations removeObject:animationDriver]; + } + } + + [self stopAnimationLoopIfNeeded]; +} + + +#pragma mark -- Updates + +- (void)updateAnimations +{ + [_animationNodes enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, RCTAnimatedNode *node, BOOL *stop) { + if (node.needsUpdate) { + [node updateNodeIfNecessary]; + } + }]; +} + +@end diff --git a/Libraries/NativeAnimation/RCTViewPropertyMapper.h b/Libraries/NativeAnimation/RCTViewPropertyMapper.h index 791825c938bddb..6f2a98f116ed5a 100644 --- a/Libraries/NativeAnimation/RCTViewPropertyMapper.h +++ b/Libraries/NativeAnimation/RCTViewPropertyMapper.h @@ -9,14 +9,14 @@ #import -@class RCTNativeAnimatedModule; +@class RCTUIManager; @interface RCTViewPropertyMapper : NSObject @property (nonatomic, readonly) NSNumber *viewTag; - (instancetype)initWithViewTag:(NSNumber *)viewTag - animationModule:(RCTNativeAnimatedModule *)animationModule NS_DESIGNATED_INITIALIZER; + uiManager:(RCTUIManager *)uiManager NS_DESIGNATED_INITIALIZER; - (void)updateViewWithDictionary:(NSDictionary *)updates; diff --git a/Libraries/NativeAnimation/RCTViewPropertyMapper.m b/Libraries/NativeAnimation/RCTViewPropertyMapper.m index 727d03d79154b1..40c8b5625935cb 100644 --- a/Libraries/NativeAnimation/RCTViewPropertyMapper.m +++ b/Libraries/NativeAnimation/RCTViewPropertyMapper.m @@ -17,41 +17,46 @@ #import "RCTNativeAnimatedModule.h" +@interface RCTViewPropertyMapper () + +@property (nonatomic, weak) UIView *cachedView; +@property (nonatomic, weak) RCTUIManager *uiManager; + +@end + @implementation RCTViewPropertyMapper -{ - RCTNativeAnimatedModule *_animationModule; -} - (instancetype)initWithViewTag:(NSNumber *)viewTag - animationModule:(RCTNativeAnimatedModule *)animationModule + uiManager:(RCTUIManager *)uiManager { if ((self = [super init])) { - _animationModule = animationModule; + _uiManager = uiManager; _viewTag = viewTag; - _animationModule = animationModule; } return self; } RCT_NOT_IMPLEMENTED(- (instancetype)init) -- (void)updateViewWithDictionary:(NSDictionary *)updates +- (void)updateViewWithDictionary:(NSDictionary *)properties { - if (!updates.count) { + // cache the view for perf reasons (avoid constant lookups) + UIView *view = _cachedView = _cachedView ?: [self.uiManager viewForReactTag:_viewTag]; + if (!view) { + RCTLogError(@"No view to update."); return; } - UIView *view = [_animationModule.bridge.uiManager viewForReactTag:_viewTag]; - if (!view) { + if (!properties.count) { return; } - NSNumber *opacity = [RCTConvert NSNumber:updates[@"opacity"]]; + NSNumber *opacity = [RCTConvert NSNumber:properties[@"opacity"]]; if (opacity) { view.alpha = opacity.floatValue; } - NSObject *transform = updates[@"transform"]; + NSObject *transform = properties[@"transform"]; if ([transform isKindOfClass:[NSValue class]]) { view.layer.allowsEdgeAntialiasing = YES; view.layer.transform = ((NSValue *)transform).CATransform3DValue;