Add support for native animated events on iOS

Summary:
This adds native support for `Animated.event` on iOS.

**Test plan**
Tested in the native animated UIExplorer example that it works properly like on Android.
Closes https://github.com/facebook/react-native/pull/9598

Differential Revision: D4110331

fbshipit-source-id: 15748d23d0f475f2bcd1040ca3dca33e2620f058
This commit is contained in:
James Ide
2016-11-01 03:58:53 -07:00
committed by Facebook Github Bot
parent 0fe1c7a9ff
commit fc11a5fde8
9 changed files with 215 additions and 18 deletions
@@ -15,6 +15,7 @@
#import "RCTAnimationUtils.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTEventAnimation.h"
#import "RCTInterpolationAnimatedNode.h"
#import "RCTLog.h"
#import "RCTDiffClampAnimatedNode.h"
@@ -30,6 +31,7 @@
{
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes;
NSMutableDictionary<NSNumber *, id<RCTAnimationDriver>> *_animationDrivers;
NSMutableDictionary<NSString *, RCTEventAnimation *> *_eventAnimationDrivers;
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations;
NSMutableSet<id<RCTAnimationDriver>> *_finishedAnimations;
NSMutableSet<RCTValueAnimatedNode *> *_updatedValueNodes;
@@ -45,10 +47,18 @@ RCT_EXPORT_MODULE()
_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];
}
@@ -157,7 +167,7 @@ RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId
[_activeAnimations addObject:animationDriver];
_animationDrivers[animationId] = animationDriver;
[animationDriver startAnimation];
[self startAnimation];
[self startAnimationLoopIfNeeded];
}
RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)animationId)
@@ -263,12 +273,64 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
}
}
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)}];
}
- (BOOL)eventDispatcherWillDispatchEvent:(id<RCTEvent>)event
{
// Native animated events only work for events dispatched from the main queue.
if (!RCTIsMainQueue() || _eventAnimationDrivers.count == 0) {
return NO;
}
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName];
RCTEventAnimation *driver = _eventAnimationDrivers[key];
if (driver) {
[driver updateWithEvent:event];
[self updateViewsProps];
[driver.valueNode cleanupAnimationUpdate];
return YES;
}
return NO;
}
- (void)updateViewsProps
{
for (RCTPropsAnimatedNode *propsNode in _propAnimationNodes) {
@@ -278,14 +340,22 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
#pragma mark -- Animation Loop
- (void)startAnimation
- (void)startAnimationLoopIfNeeded
{
if (!_displayLink && _activeAnimations.count > 0) {
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
@@ -321,10 +391,7 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
}
[_finishedAnimations removeAllObjects];
if (_activeAnimations.count == 0) {
[_displayLink invalidate];
_displayLink = nil;
}
[self stopAnimationLoopIfNeeded];
}
@end