Presenting content on a connected display
Fill connected displays with additional content from your app.
Overview
The user can connect additional displays to an iOS device at any time using AirPlay or a physical cable. When an external display connects, the system mirrors your app’s primary display, or, on compatible iPad models with extended display enabled, presents your app’s interactive windows on the external display.
Each additional display represents new space on which to present your app’s content. To present content on a connected display, you attach windows to UIWindowScene objects that the system provides and respond to life-cycle events using scene delegates.
UIKit defines different UISceneSession.Role types to indicate how the user interacts with the content for a scene. The following types relate to content displayed on a connected screen:
Scenes with the windowApplication role present interactive windows. The windows render on the built-in display or a connected display for some iPad models. Windows for more than one scene may present concurrently and may not take up the full screen.
Scenes with the windowExternalDisplayNonInteractive role present noninteractive windows on connected displays. When an app presents content for this scene, it spans the full screen.
Configure your project for scenes
You provide the system with scene configurations to specify the information it uses to create scenes for your app and indicate the types of scenes your app supports.
By default, Xcode preconfigures new projects to use scenes with the windowApplication role. iPad models with the M1 chip can present interactive content on the connected screen through this type of scene, when using Stage Manager, an external keyboard, and a pointing device.
Register a scene accessory to present noninteractive content on the connected display that supplements the interactive content your app presents on the built-in screen. For example, a game might show its content on a connected display and show game controls on the built-in screen.
[Image]
For more information, see Specifying the scenes your app supports.
Register a scene accessory for noninteractive content
A scene accessory declares supplementary content that the system presents on your app’s behalf when associated functionality becomes available, such as an external display connected by cable or AirPlay. Your app declares what content to provide, and the system decides when and where to present it. Because the content appears only when a display is available, design your app to remain fully functional without the external display.
Beginning in iOS 27, your app receives a scene with the windowExternalDisplayNonInteractive role only after it registers a scene accessory. In earlier releases, the system connected this scene automatically, and your app opted out by declining to provide content for it. If your app previously provided content by checking the connecting scene’s role in application(_:configurationForConnecting:options:) or your scene delegate, remove that role-specific logic and register a scene accessory instead. You can reuse your existing scene delegate as the accessory’s delegate class, or create a new one dedicated to the external-display scene.
To register a scene accessory, create a UISceneAccessory for noninteractive external-display content and register it on a view controller in your app’s main interface. Choose the view controller whose content the external display supplements, then pass a UISceneConfiguration that identifies the scene delegate to use, either by setting its delegateClass directly or by giving it a name that matches a configuration in your information property list scene manifest. Registering returns a UISceneAccessoryRegistration that you keep to observe and control the accessory:
class PlayerViewController: UIViewController {
var displayRegistration: UISceneAccessoryRegistration?
override func viewDidLoad() {
super.viewDidLoad()
// Describe the scene to present, including the delegate that attaches its window.
let configuration = UISceneConfiguration()
configuration.delegateClass = ExternalDisplaySceneDelegate.self
// Register the accessory so the system can present this content on an external display.
let accessory = UISceneAccessory.externalNonInteractive(sceneConfiguration: configuration)
displayRegistration = registerSceneAccessory(accessory)
}
}While your app presents the view controller, the registration is enabled, and an external display is available, the system connects the noninteractive external-display scene. The system then calls scene(_:willConnectTo:options:) on the scene delegate you specified, where you attach a UIWindow to present your content. Content for this scene spans the full screen. When the app dismisses the view controller or the display disconnects, the system disconnects the scene and calls sceneDidDisconnect(_:).
If more than one view controller in your app registers an external-display accessory, the system presents the registration that belongs to the topmost, most recently presented view controller.
Use the UISceneAccessoryRegistration to control and observe the accessory:
Set isEnabled to
falseto stop presenting your content without giving up the registration, and set it back totrueto resume.Read isAvailable to find out whether the system can currently present the accessory. This property supports observation tracking in updateProperties() and layoutSubviews().
To stop offering the content entirely, unregister the accessory with unregisterSceneAccessory(_:). If the system is presenting the accessory at the time, it dismisses the scene:
if let displayRegistration {
unregisterSceneAccessory(displayRegistration)
self.displayRegistration = nil
}To pass additional context to the scene delegate when the scene connects, create the accessory with externalNonInteractive(sceneConfiguration:userInfo:) and read the value from sceneAccessoryUserInfo in scene(_:willConnectTo:options:).
Attach a window to a scene
Your scene delegate receives the connecting scene through its scene(_:willConnectTo:options:) method. Use the method and the session object it provides to configure and attach a window to the scene. The system displays the window you provide on the window scene’s current screen.
This example configures a window to render on a scene’s display.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
self.window = window
window.makeKeyAndVisible()
}When using a storyboard, the system initializes and attaches a window to the scene for you.
In addition to calling this method, UIKit also posts the willConnectNotification notification.
Handle disconnection
When the system disconnects a scene from your app, which may occur if a user disconnects the display, your app receives a call through the sceneDidDisconnect(_:) method on the scene’s delegate. Use this method to perform any final cleanup and update the content other scenes present, when necessary.
UIKit also posts the didDisconnectNotification notification in addition to calling this method.
Handle transitions to and from connected displays
Scenes may appear on different displays during their lifetime. If you use information from a UIScreen object, obtain it contextually using a windows scene’s screen property.
Use the scene delegate’s windowScene(_:didUpdate:interfaceOrientation:traitCollection:) method if your app needs to know when a scene is changing screens.
This example uses a display link and updates the link when the scene changes screens.
class ExternalDisplaySceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var screen: UIScreen?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
if session.role == .windowExternalDisplayNonInteractive {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ExternalDisplayViewController()
self.window = window
window.makeKeyAndVisible()
...
setupDisplayLinkIfNecessary()
}
}
func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
setupDisplayLinkIfNecessary()
}
weak var linkedScreen: UIScreen?
func setupDisplayLinkIfNecessary() {
let currentScreen = self.screen
if currentScreen != linkedScreen {
// Set up display link
...
self.linkedScreen = currentScreen
}
}
...
}Change the screen mode of an external display
Many displays support multiple resolutions, some of which use different pixel aspect ratios. Screen objects use the most common screen mode by default, but they support changing that mode when they display content for a scene with the windowExternalDisplayNonInteractive role. For example, if you’re implementing a game using textures for a 640 x 480 pixel screen, you might change the screen mode for screens with higher default resolutions. Don’t attempt to change the mode of a screen for a scene with the windowApplication role.
If you plan to use a screen mode other than the default one, apply that mode to the UIScreen object before associating the screen with a window. The UIScreenMode class defines the attributes of a single screen mode. You can get a list of the modes supported by a screen from its availableModes property and then iterate through the list for one that matches your needs.
For more information about screen modes, see UIScreenMode.
See Also
Related Documentation
Multitasking on iPad, Mac, and Apple Vision ProManaging your app’s life cycleBuilding a desktop-class iPad app