*Platform: iOS 13.0+
This package is able to use <ExternalDisplay>
for Split View or Multiple Windows on iPad, currently tested on the Designed for iPad
targets: iPadOS, visionOS (Currently not support on macOS).
Please visit Notice section first to know the current limitation of this feature.
The following screenshots are the example from IPadMultiScenesHeadlessExample, it's very simple browser implementation:
iOS Simulator (
iPad Pro (11-inch)
)
visionOS Simulator (
Apple Vision Pro (Designed for iPad)
)
It requires some setup of the app project.
To support multiple scenes, these are two steps need to setup in your Xcode project:
- Edit Xcode workspace: Add iPad target in
Supported Destinations
:
- Edit
Info.plist
for enable multiple scenes & disable full screen:
--- Info.prev.plist 2023-06-26 13:53:27
+++ Info.plist 2023-06-26 13:52:22
@@ -37,6 +37,13 @@
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
+ <key>UIApplicationSceneManifest</key>
+ <dict>
+ <key>UIApplicationSupportsMultipleScenes</key>
+ <true/>
+ </dict>
+ <key>UIRequiresFullScreen</key>
+ <false/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
- Edit
AppDelegate.mm
to use UIScene:
--- AppDelegate.prev.mm 2023-06-27 09:14:18
+++ AppDelegate.mm 2023-06-27 09:14:39
@@ -2,6 +2,9 @@
#import <React/RCTBundleURLProvider.h>
+// Use RNExternalAppDelegateUtil
+#import "RNExternalDisplayUtils.h"
+
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
@@ -14,6 +17,19 @@
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
+- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) {
+ UISceneConfiguration * configuration =
+ [RNExternalAppDelegateUtil application:application
+ configurationForConnectingSceneSession:connectingSceneSession
+ options:options
+ sceneOptions:@{
+ @"headless": @NO
+ }
+ ];
+ // You can put custom configuration here
+ return configuration;
+}
+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
This setup the two scenes:
RNExternalSceneMainDelegate
: The main scene of the app. Please make sure it only creates one unless you're project able to building multiple react-native bridge hostsRNExternalSceneDelegate
: The scene for external screen, it can be multiple.
The behavior of Create Window
in the scene delegates setup:
- Create main scene with
RNExternalSceneMainDelegate
if the main scene is not active (App initialization or the main scene is closed) - Create scene with
RNExternalSceneDelegate
if:- Main scene is active
- Connected to a external screen
- Call the
requestScene(options)
method
You can customize the scene configuration in application:configurationForConnectingSceneSession:options:
method. Please make sure there is only one main scene can be active at the same time.
Example:
import { SceneManager } from 'react-native-external-display';
const isMultipleScenesAvailable = SceneManager.isAvailable();
// Request new scene
SceneManager.requestScene({
windowBackgroundColor: '#cccccc', // Default to black
userInfo: {
// Custom data, that can be accessed in `screen.userInfo` from getScreens()
},
})
// Close scene (sceneId can be get from getScreens())
SceneManager.closeScene(sceneId);
SceneManager.isMainSceneActive(); // Check if the main scene not closed
SceneManager.resumeMainScene(); // Resume the main scene if it is closed
The scene resizable is depends on the orientation setting of the app. You can change the orientation setting in Info.plist
:
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
The main scene can be closed and the app will be still running if another scenes is active.
You will need to resume the main scene if you want to use it again:
- Create Window by click app icon on dock, then click the top-left
+
button - Call the
resumeMainScene(options)
method, you can also check the status byisMainSceneActive()
method
For a multi-window app with exactly the same layout, we can consider that we don't need to have main scene, and make the root of the main app is composed of multiple <ExternalDisplay>
:
- Edit
AppDelegate.mm
: Useheadless: @YES
scene option (UISceneConfiguration * configuration = [RNExternalAppDelegateUtil application:application configurationForConnectingSceneSession:connectingSceneSession options:options sceneOptions:@{ @"headless": @YES }];
) so it will not create the main scene to show the root view. - Make sure your app can automatically create new scene & render views by
<ExternalDisplay>
. - Exclude external screen by check
screen.type == SceneManager.types.EXTERNAL_DISPLAY
, and you can use it for other purposes
Example: IPadMultiScenesHeadlessExample
You can use Split View feature by default, or use Stage Manager (iOS 16.0+).
To enable Stage Manager on iPad simulator:
- Option 1 - Setting: Choose Home Screen & Multitasking. Tap Stage Manager at the bottom of the Home Screen & Multitasking screen.
- Option 2 - Command:
xcrun simctl spawn booted defaults write -g SBChamoisWindowingEnabled -bool true
These are some issues may support/fix in the future or not:
- Drag-and-Drop to create new scene is not supported yet
- The multiple scenes not working on macOS (Tested on 13.4),
requestSceneSessionActivation
always failed evenUIApplicationSupportsMultipleScenes
is enabled - SafeAreaView has no effect on new scenes