diff --git a/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider+Private.h b/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider+Private.h new file mode 100644 index 000000000000..062cd4e106e0 --- /dev/null +++ b/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider+Private.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#import + +@interface RCTAnimatedModuleProvider (Private) + +/** + * When true, animated frames are skipped while the application is inactive (between + * `UIApplicationWillEnterForeground` -> `UIApplicationDidBecomeActive`), default is false. + */ +- (instancetype)initWithSkipFramesDuringForegroundTransition:(BOOL)skipFramesDuringForegroundTransition; + +@end diff --git a/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider.mm b/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider.mm index 17914749a512..71a3c8d9e147 100644 --- a/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider.mm +++ b/packages/react-native/ReactApple/RCTAnimatedModuleProvider/RCTAnimatedModuleProvider.mm @@ -7,12 +7,15 @@ #import "RCTAnimatedModuleProvider.h" +#import "RCTAnimatedModuleProvider+Private.h" + #import #if TARGET_OS_OSX #import #else #import +#import #endif #import @@ -28,6 +31,21 @@ @implementation RCTAnimatedModuleProvider { std::function _onRender; std::weak_ptr _nativeAnimatedNodesManagerProvider; + + BOOL _skipFramesDuringForegroundTransition; +} + +- (instancetype)init +{ + return [self initWithSkipFramesDuringForegroundTransition:NO]; +} + +- (instancetype)initWithSkipFramesDuringForegroundTransition:(BOOL)skipFramesDuringForegroundTransition +{ + if (self = [super init]) { + _skipFramesDuringForegroundTransition = skipFramesDuringForegroundTransition; + } + return self; } - (void)dealloc @@ -61,6 +79,15 @@ - (void)_onDisplayLinkTick // use-after-free during hot reload. The provider must remain alive for the // entire duration of _onRender() since it holds references to the animation // nodes manager and related data structures. +#if !TARGET_OS_OSX + // See initWithSkipFramesDuringForegroundTransition: the frame is skipped, + // never the clock. + if (_skipFramesDuringForegroundTransition && + UIApplication.sharedApplication.applicationState == UIApplicationStateInactive) { + return; + } +#endif + auto strongProvider = _nativeAnimatedNodesManagerProvider.lock(); if (strongProvider != nullptr && _displayLink != nullptr && _onRender != nullptr) { _onRender();