From 41c788a2b49b77148a85fe8c6d3f7cab1d04800f Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Mon, 26 Oct 2020 15:35:41 -0700 Subject: [PATCH] Make sure opacity and transform are applied in native animation Summary: Changelog: [internal] Problem: `props.transform` gets out of sync with `self.layer.transform`. To avoid writing out value of transform matrix, in the follow example I will only consider identity matrix and non-identity matrix. It is sufficient to demonstrate the point. 1. View is reused with transform being something besides identity. This causes `props.transform` and `self.layer.transform` to be non-identity. 2. View is taken from pool and animated with transform set to non-identity. 3. React JS props arrive and set `view.props` to identity but `self.layer.transform` stays unchanged because it is managed by native animation. This is the point where `props.transform` and `self.layer.transform` get out of sync. 4. Native animation wants to set transform to identity to finish the animation. But inside `[RCTViewComponentView updateProps:oldProps:]` `self.layer.transform` does not get set because current `view.props` is already identity. Solution: After native animation layer calls `[RCTViewComponentView updateProps:oldProps:]`, verify that values were set. If they weren't, set them directly without depending on `[RCTViewComponentView updateProps:oldProps:]`. Reviewed By: JoshuaGross Differential Revision: D24538442 fbshipit-source-id: ba8c59c5c9bb751306118bd1c7f0ccd9d0fb7fba --- React/Fabric/Mounting/RCTMountingManager.mm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/React/Fabric/Mounting/RCTMountingManager.mm b/React/Fabric/Mounting/RCTMountingManager.mm index 2f7363c6da9ea7..387542399fc409 100644 --- a/React/Fabric/Mounting/RCTMountingManager.mm +++ b/React/Fabric/Mounting/RCTMountingManager.mm @@ -11,6 +11,7 @@ #import #import +#import #import #import #import @@ -228,6 +229,19 @@ - (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = nil; [componentView updateProps:newProps oldProps:oldProps]; componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = propKeys; + + const auto &newViewProps = *std::static_pointer_cast(newProps); + + if (props[@"transform"] && + !CATransform3DEqualToTransform( + RCTCATransform3DFromTransformMatrix(newViewProps.transform), componentView.layer.transform)) { + RCTLogWarn(@"transform was not applied during [RCTViewComponentView updateProps:oldProps:]"); + componentView.layer.transform = RCTCATransform3DFromTransformMatrix(newViewProps.transform); + } + if (props[@"opacity"] && componentView.layer.opacity != (float)newViewProps.opacity) { + RCTLogWarn(@"opacity was not applied during [RCTViewComponentView updateProps:oldProps:]"); + componentView.layer.opacity = newViewProps.opacity; + } } - (void)synchronouslyDispatchCommandOnUIThread:(ReactTag)reactTag