diff --git a/React/Fabric/RCTScheduler.h b/React/Fabric/RCTScheduler.h index d38465591fe..f85174c03f3 100644 --- a/React/Fabric/RCTScheduler.h +++ b/React/Fabric/RCTScheduler.h @@ -64,6 +64,12 @@ NS_ASSUME_NONNULL_BEGIN - (facebook::react::MountingCoordinator::Shared)mountingCoordinatorWithSurfaceId:(facebook::react::SurfaceId)surfaceId; +- (void)onAnimationStarted; + +- (void)onAllAnimationsComplete; + +- (void)animationTick; + @end NS_ASSUME_NONNULL_END diff --git a/React/Fabric/RCTScheduler.mm b/React/Fabric/RCTScheduler.mm index 8c876c379ff..d89af23b385 100644 --- a/React/Fabric/RCTScheduler.mm +++ b/React/Fabric/RCTScheduler.mm @@ -7,10 +7,12 @@ #import "RCTScheduler.h" +#import #import #import #import #import +#include #import @@ -61,23 +63,82 @@ class SchedulerDelegateProxy : public SchedulerDelegate { void *scheduler_; }; +class LayoutAnimationDelegateProxy : public LayoutAnimationStatusDelegate, public RunLoopObserver::Delegate { + public: + LayoutAnimationDelegateProxy(void *scheduler) : scheduler_(scheduler) {} + virtual ~LayoutAnimationDelegateProxy() {} + + void onAnimationStarted() override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler onAnimationStarted]; + } + + /** + * Called when the LayoutAnimation engine completes all pending animations. + */ + void onAllAnimationsComplete() override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler onAllAnimationsComplete]; + } + + void activityDidChange(RunLoopObserver::Delegate const *delegate, RunLoopObserver::Activity activity) const + noexcept override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler animationTick]; + } + + private: + void *scheduler_; +}; + @implementation RCTScheduler { std::shared_ptr _scheduler; + std::shared_ptr _animationDriver; std::shared_ptr _delegateProxy; + std::shared_ptr _layoutAnimationDelegateProxy; + RunLoopObserver::Unique _uiRunLoopObserver; + BOOL _layoutAnimationsEnabled; } - (instancetype)initWithToolbox:(facebook::react::SchedulerToolbox)toolbox { if (self = [super init]) { + auto reactNativeConfig = + toolbox.contextContainer->at>("ReactNativeConfig"); + _layoutAnimationsEnabled = reactNativeConfig->getBool("react_fabric:enabled_layout_animations_ios"); + _delegateProxy = std::make_shared((__bridge void *)self); - _scheduler = std::make_shared(toolbox, nullptr, _delegateProxy.get()); + + if (_layoutAnimationsEnabled) { + _layoutAnimationDelegateProxy = std::make_shared((__bridge void *)self); + _animationDriver = std::make_unique(_layoutAnimationDelegateProxy.get()); + _uiRunLoopObserver = + toolbox.mainRunLoopObserverFactory(RunLoopObserver::Activity::BeforeWaiting, _layoutAnimationDelegateProxy); + _uiRunLoopObserver->setDelegate(_layoutAnimationDelegateProxy.get()); + } + + _scheduler = std::make_shared( + toolbox, (_animationDriver ? _animationDriver.get() : nullptr), _delegateProxy.get()); } return self; } +- (void)animationTick +{ + _scheduler->animationTick(); +} + - (void)dealloc { + if (_animationDriver) { + _animationDriver->setLayoutAnimationStatusDelegate(nullptr); + } + _animationDriver = nullptr; + _scheduler->setDelegate(nullptr); } @@ -90,7 +151,13 @@ class SchedulerDelegateProxy : public SchedulerDelegate { SystraceSection s("-[RCTScheduler startSurfaceWithSurfaceId:...]"); auto props = convertIdToFollyDynamic(initialProps); - _scheduler->startSurface(surfaceId, RCTStringFromNSString(moduleName), props, layoutConstraints, layoutContext); + _scheduler->startSurface( + surfaceId, + RCTStringFromNSString(moduleName), + props, + layoutConstraints, + layoutContext, + (_animationDriver ? _animationDriver.get() : nullptr)); _scheduler->renderTemplateToSurface( surfaceId, props.getDefault("navigationConfig").getDefault("initialUITemplate", "").getString()); } @@ -127,4 +194,18 @@ class SchedulerDelegateProxy : public SchedulerDelegate { return _scheduler->findMountingCoordinator(surfaceId); } +- (void)onAnimationStarted +{ + if (_uiRunLoopObserver) { + _uiRunLoopObserver->enable(); + } +} + +- (void)onAllAnimationsComplete +{ + if (_uiRunLoopObserver) { + _uiRunLoopObserver->disable(); + } +} + @end