Allow the app to control the Activity Indicator in Bridgeless mode (#43195)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43195

Right now, the activity indicator is automatically hidden when the view is ready to be shown in bridgeless mode.
There is no way to prevent that the activity indicator is automatically removed.

In OSS, we have libraries (e.g.: `react-native-bootsplash`) that will allow the app to control when and how dismiss the splashscreen, but due to the current automatic behavior on Bridgeless, they stopped working.

***Note:** In the previous implementation, they were working because instead of using the `loadingView` property, they were adding the splashscreen view on top of the existing one. However, with the lazy behavior of the bridgeless mode, this is not working anymore because the RCTMountingManager [expect not to have any subview](https://www.internalfb.com/code/fbsource/[6962fa457dbc74ab3a760cf6090d9643c6748781]/xplat/js/react-native-github/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm?lines=176) when the first surface is mounted.*

## Changelog
[iOS][Added] - Allow the activityIndicator to be controlled from JS in bridgeless mode

Reviewed By: philIip

Differential Revision: D54191856

fbshipit-source-id: 14738032f04adf7eaf7d200d889acd752aed0ed3
This commit is contained in:
Riccardo Cipolleschi
2024-03-05 09:37:26 -08:00
committed by Alex Hunt
parent 768cf6e79c
commit ec48cb6560
2 changed files with 24 additions and 2 deletions
@@ -56,6 +56,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, copy, nullable) RCTSurfaceHostingViewActivityIndicatorViewFactory activityIndicatorViewFactory;
/**
* When set to `YES`, the activity indicator is not automatically hidden when the Surface stage changes.
* In this scenario, users should invoke `hideActivityIndicator` to remove it.
*
* @param disabled: if `YES`, the auto-hide is disabled. Otherwise the loading view will be hidden automatically
*/
- (void)disableActivityIndicatorAutoHide:(BOOL)disabled;
@end
NS_ASSUME_NONNULL_END
@@ -24,6 +24,7 @@
UIView *_Nullable _activityIndicatorView;
UIView *_Nullable _surfaceView;
RCTSurfaceStage _stage;
BOOL _autoHideDisabled;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
@@ -36,6 +37,7 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
if (self = [super initWithFrame:CGRectZero]) {
_surface = surface;
_sizeMeasureMode = sizeMeasureMode;
_autoHideDisabled = NO;
_surface.delegate = self;
_stage = surface.stage;
@@ -124,6 +126,10 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
_sizeMeasureMode = sizeMeasureMode;
[self _invalidateLayout];
}
- (void)disableActivityIndicatorAutoHide:(BOOL)disabled
{
_autoHideDisabled = disabled;
}
#pragma mark - isActivityIndicatorViewVisible
@@ -162,7 +168,16 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
_surfaceView = _surface.view;
_surfaceView.frame = self.bounds;
_surfaceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:_surfaceView];
if (_activityIndicatorView && _autoHideDisabled) {
// The activity indicator is still showing and the surface is set to
// prevent the auto hide. This means that the application will take care of
// hiding it when it's ready.
// Let's add the surfaceView below the activity indicator so it's ready once
// the activity indicator is hidden.
[self insertSubview:_surfaceView belowSubview:_activityIndicatorView];
} else {
[self addSubview:_surfaceView];
}
} else {
[_surfaceView removeFromSuperview];
_surfaceView = nil;
@@ -204,7 +219,7 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
- (void)_updateViews
{
self.isSurfaceViewVisible = RCTSurfaceStageIsRunning(_stage);
self.isActivityIndicatorViewVisible = RCTSurfaceStageIsPreparing(_stage);
self.isActivityIndicatorViewVisible = _autoHideDisabled || RCTSurfaceStageIsPreparing(_stage);
}
- (void)didMoveToWindow