diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index 5cb8743fa..51ba4c3d7 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -2187,7 +2187,13 @@ function attachNativeEvent(viewRef: any, eventName: string, argMapping: Array { + NativeAnimatedAPI.removeAnimatedEventFromView( + viewTag, + eventName, + mapping.animatedValueTag, + ); + }); }, }; } diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m index 55763bfe5..e64e65d48 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m @@ -169,9 +169,10 @@ RCT_EXPORT_METHOD(addAnimatedEventToView:(nonnull NSNumber *)viewTag RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag eventName:(nonnull NSString *)eventName) + animatedNodeTag:(nonnull NSNumber *)animatedNodeTag { [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { - [nodesManager removeAnimatedEventFromView:viewTag eventName:eventName]; + [nodesManager removeAnimatedEventFromView:viewTag eventName:eventName animatedNodeTag:animatedNodeTag]; }]; } diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index e911f1b37..a3fb93055 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -70,7 +70,8 @@ eventMapping:(NSDictionary *__nonnull)eventMapping; - (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag - eventName:(nonnull NSString *)eventName; + eventName:(nonnull NSString *)eventName + animatedNodeTag:(nonnull NSNumber *)animatedNodeTag; - (void)handleAnimatedEvent:(nonnull id)event; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m index ac15ad705..a4be77178 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m @@ -33,7 +33,9 @@ { RCTUIManager *_uiManager; NSMutableDictionary *_animationNodes; - NSMutableDictionary *_eventDrivers; + // Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time + // there will be only one driver per mapping so all code code should be optimized around that. + NSMutableDictionary *> *_eventDrivers; NSMutableSet> *_activeAnimations; CADisplayLink *_displayLink; } @@ -264,15 +266,36 @@ NSArray *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]]; RCTEventAnimation *driver = - [[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node]; + [[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node]; - _eventDrivers[[NSString stringWithFormat:@"%@%@", viewTag, eventName]] = driver; + NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName]; + if (_eventDrivers[key] != nil) { + [_eventDrivers[key] addObject:driver]; + } else { + NSMutableArray *drivers = [NSMutableArray new]; + [drivers addObject:driver]; + _eventDrivers[key] = drivers; + } } - (void)removeAnimatedEventFromView:(nonnull NSNumber *)viewTag eventName:(nonnull NSString *)eventName + animatedNodeTag:(nonnull NSNumber *)animatedNodeTag { - [_eventDrivers removeObjectForKey:[NSString stringWithFormat:@"%@%@", viewTag, eventName]]; + NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName]; + if (_eventDrivers[key] != nil) { + if (_eventDrivers[key].count == 1) { + [_eventDrivers removeObjectForKey:key]; + } else { + NSMutableArray *driversForKey = _eventDrivers[key]; + for (NSUInteger i = 0; i < driversForKey.count; i++) { + if (driversForKey[i].valueNode.nodeTag == animatedNodeTag) { + [driversForKey removeObjectAtIndex:i]; + break; + } + } + } + } } - (void)handleAnimatedEvent:(id)event @@ -282,9 +305,12 @@ } NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName]; - RCTEventAnimation *driver = _eventDrivers[key]; - if (driver) { - [driver updateWithEvent:event]; + NSMutableArray *driversForKey = _eventDrivers[key]; + if (driversForKey) { + for (RCTEventAnimation *driver in driversForKey) { + [driver updateWithEvent:event]; + } + [self updateAnimations]; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index c5e2aa04f..a3551bd10 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -351,11 +351,11 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule implements } @ReactMethod - public void removeAnimatedEventFromView(final int viewTag, final String eventName) { + public void removeAnimatedEventFromView(final int viewTag, final String eventName, final int animatedValueTag) { mOperations.add(new UIThreadOperation() { @Override public void execute(NativeAnimatedNodesManager animatedNodesManager) { - animatedNodesManager.removeAnimatedEventFromView(viewTag, eventName); + animatedNodesManager.removeAnimatedEventFromView(viewTag, eventName, animatedValueTag); } }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 12c7f640b..6251b8f4d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -31,7 +31,9 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Queue; @@ -55,12 +57,14 @@ import javax.annotation.Nullable; private final SparseArray mAnimatedNodes = new SparseArray<>(); private final SparseArray mActiveAnimations = new SparseArray<>(); private final SparseArray mUpdatedNodes = new SparseArray<>(); - private final Map mEventDrivers = new HashMap<>(); + // Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time + // there will be only one driver per mapping so all code code should be optimized around that. + private final Map> mEventDrivers = new HashMap<>(); private final Map> mCustomEventTypes; private final UIImplementation mUIImplementation; private int mAnimatedGraphBFSColor = 0; - // Used to avoid allocating a new array on every frame in runUpdates. - private final List mRunUpdateNodeList = new ArrayList<>(); + // Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`. + private final List mRunUpdateNodeList = new LinkedList<>(); public NativeAnimatedNodesManager(UIManagerModule uiManager) { mUIImplementation = uiManager.getUIImplementation(); @@ -312,11 +316,32 @@ import javax.annotation.Nullable; } EventAnimationDriver event = new EventAnimationDriver(pathList, (ValueAnimatedNode) node); - mEventDrivers.put(viewTag + eventName, event); + String key = viewTag + eventName; + if (mEventDrivers.containsKey(key)) { + mEventDrivers.get(key).add(event); + } else { + List drivers = new ArrayList<>(1); + drivers.add(event); + mEventDrivers.put(key, drivers); + } } - public void removeAnimatedEventFromView(int viewTag, String eventName) { - mEventDrivers.remove(viewTag + eventName); + public void removeAnimatedEventFromView(int viewTag, String eventName, int animatedValueTag) { + String key = viewTag + eventName; + if (mEventDrivers.containsKey(key)) { + List driversForKey = mEventDrivers.get(key); + if (driversForKey.size() == 1) { + mEventDrivers.remove(viewTag + eventName); + } else { + ListIterator it = driversForKey.listIterator(); + while (it.hasNext()) { + if (it.next().mValueNode.mTag == animatedValueTag) { + it.remove(); + break; + } + } + } + } } @Override @@ -334,11 +359,14 @@ import javax.annotation.Nullable; eventName = customEventType.get("registrationName"); } - EventAnimationDriver eventDriver = mEventDrivers.get(event.getViewTag() + eventName); - if (eventDriver != null) { - event.dispatch(eventDriver); - - updateNodes(Collections.singletonList((AnimatedNode) eventDriver.mValueNode)); + List driversForKey = mEventDrivers.get(event.getViewTag() + eventName); + if (driversForKey != null) { + for (EventAnimationDriver driver : driversForKey) { + event.dispatch(driver); + mRunUpdateNodeList.add(driver.mValueNode); + } + updateNodes(mRunUpdateNodeList); + mRunUpdateNodeList.clear(); } } }