Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "hidden" prop to Screen to support iOS Picture-in-Picture #1665

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions guides/GUIDE_FOR_LIBRARY_AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ Whether the keyboard should hide when swiping to the previous screen. Defaults t

Whether the home indicator should be hidden on this screen. Defaults to `false`.

#### `hidden` (iOS only)

If a screen is marked `hidden`, the resources will be retained, but it will not be rendered
in the stack. This is to support iOS picture-in-picture, where you have a video overlay originating
on a screen, and even when popping the screen off the stack the resources need to be retained
(not garbage collected), so that the video can continue playing. Additionally, this allows the
screen to be "un-hidden" (visually, pushed back onto the stack) when the user taps on the overlay video.

### `nativeBackButtonDismissalEnabled` (Android only)

Boolean indicating whether, when the Android default back button is clicked, the `pop` action should be performed on the native side or on the JS side to be able to prevent it.
Expand Down
1 change: 1 addition & 0 deletions ios/RNSScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ namespace react = facebook::react;

@property (nonatomic, retain) NSNumber *transitionDuration;
@property (nonatomic, readonly) BOOL dismissed;
@property (nonatomic) BOOL hidden;
@property (nonatomic) BOOL hideKeyboardOnSwipe;
@property (nonatomic) BOOL customAnimationOnSwipe;
@property (nonatomic) BOOL preventNativeDismiss;
Expand Down
11 changes: 11 additions & 0 deletions ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ - (void)initCommonProps
_gestureEnabled = YES;
_replaceAnimation = RNSScreenReplaceAnimationPop;
_dismissed = NO;
_hidden = NO;
_hasStatusBarStyleSet = NO;
_hasStatusBarAnimationSet = NO;
_hasStatusBarHiddenSet = NO;
Expand Down Expand Up @@ -199,6 +200,12 @@ - (void)setStackAnimation:(RNSScreenStackAnimation)stackAnimation
}
}

- (void)setHidden:(BOOL)hidden
{
_hidden = hidden;
_dismissed = NO;
}

- (void)setGestureEnabled:(BOOL)gestureEnabled
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
Expand Down Expand Up @@ -692,6 +699,7 @@ - (void)prepareForRecycle
// TODO: Make sure that there is no edge case when this should be uncommented
// _controller=nil;
_dismissed = NO;
_hidden = NO;
_state.reset();
_touchHandler = nil;

Expand All @@ -716,6 +724,8 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::

[self setGestureEnabled:newScreenProps.gestureEnabled];

[self setHidden:newScreenProps.hidden];

[self setTransitionDuration:[NSNumber numberWithInt:newScreenProps.transitionDuration]];

[self setHideKeyboardOnSwipe:newScreenProps.hideKeyboardOnSwipe];
Expand Down Expand Up @@ -1444,6 +1454,7 @@ @implementation RNSScreenManager
RCT_EXPORT_VIEW_PROPERTY(customAnimationOnSwipe, BOOL);
RCT_EXPORT_VIEW_PROPERTY(fullScreenSwipeEnabled, BOOL);
RCT_EXPORT_VIEW_PROPERTY(gestureEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(hidden, BOOL)
RCT_EXPORT_VIEW_PROPERTY(gestureResponseDistance, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(hideKeyboardOnSwipe, BOOL)
RCT_EXPORT_VIEW_PROPERTY(preventNativeDismiss, BOOL)
Expand Down
2 changes: 1 addition & 1 deletion ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ - (void)updateContainer
NSMutableArray<UIViewController *> *pushControllers = [NSMutableArray new];
NSMutableArray<UIViewController *> *modalControllers = [NSMutableArray new];
for (RNSScreenView *screen in _reactSubviews) {
if (!screen.dismissed && screen.controller != nil) {
if (!screen.dismissed && screen.controller != nil && !screen.hidden) {
if (pushControllers.count == 0) {
// first screen on the list needs to be places as "push controller"
[pushControllers addObject:screen.controller];
Expand Down
8 changes: 8 additions & 0 deletions native-stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ A Boolean to that lets you opt out of insetting the header. You may want to * se

Boolean indicating whether the navigation bar is translucent.

#### `hidden` (iOS only)

If a screen is marked `hidden`, the resources will be retained, but it will not be rendered
in the stack. This is to support iOS picture-in-picture, where you have a video overlay originating
on a screen, and even when popping the screen off the stack the resources need to be retained
(not garbage collected), so that the video can continue playing. Additionally, this allows the
screen to be "un-hidden" (visually, pushed back onto the stack) when the user taps on the overlay video.

#### `hideKeyboardOnSwipe` (iOS only)

Whether the keyboard should hide when swiping to the previous screen. Defaults to `false`.
Expand Down
1 change: 0 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export class NativeScreen extends React.Component<ScreenProps> {
}
return (
<View
// @ts-expect-error: hidden exists on web, but not in React Native
hidden={activityState === 0}
style={[style, { display: activityState !== 0 ? 'flex' : 'none' }]}
{...rest}
Expand Down
11 changes: 11 additions & 0 deletions src/native-stack/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ export type NativeStackNavigationOptions = {
* @platform ios
*/
gestureEnabled?: boolean;
/**
* If a screen is marked `hidden`, the resources will be retained, but it will not be rendered
* in the stack. This is to support iOS picture-in-picture, where you have a video overlay originating
* on a screen, and even when popping the screen off the stack the resources need to be retained
* (not garbage collected), so that the video can continue playing. Additionally, this allows the
* screen to be "un-hidden" (visually, pushed back onto the stack) when the user taps on the overlay video.
* Only supported on iOS.
*
* @platform ios
*/
hidden?: boolean;
/**
* Use it to restrict the distance from the edges of screen in which the gesture should be recognized. To be used alongside `fullScreenSwipeEnabled`.
*
Expand Down
11 changes: 11 additions & 0 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,17 @@ export interface ScreenProps extends ViewProps {
* @platform ios
*/
gestureEnabled?: boolean;
/**
* If a screen is marked `hidden`, the resources will be retained, but it will not be rendered
* in the stack. This is to support iOS picture-in-picture, where you have a video overlay originating
* on a screen, and even when popping the screen off the stack the resources need to be retained
* (not garbage collected), so that the video can continue playing. Additionally, this allows the
* screen to be "un-hidden" (visually, pushed back onto the stack) when the user taps on the overlay video.
* Only supported on iOS.
*
* @platform ios
*/
hidden?: boolean;
/**
* Use it to restrict the distance from the edges of screen in which the gesture should be recognized. To be used alongside `fullScreenSwipeEnabled`.
*
Expand Down