Managing your Metal app window in iPadOS
Set up a window that handles dynamically resizing your Metal content.
Overview
A scene represents a single instance of your app’s UI. You can choose whether people can create multiple scenes for your app. Typically, Metal apps and games support only one scene because they need priority access to the available resources on a device. On iPadOS 26 and later, people can always resize your app’s scenes if they have enabled multitasking.
Apps that don’t adopt the scene-based life cycle log a warning at startup on iOS 26 and iPadOS 26 and must be updated. In the next major release, the scene-based life cycle is required when building with the latest SDK.
For more information on migrating your iPad app, see TN3192: Migrating your iPad app from the deprecated UIRequiresFullScreen key and TN3187: Migrating to the UIKit scene-based life cycle.
Create the window
Manage windows on iPad by using UIWindowScene for UIKit and Scene for SwiftUI. To configure a UIWindow under a scene you assign a content view controller and embed your Metal view inside the controller.
To configure scene support for your Metal project:
Open the Xcode project.
Select the project in the Project navigator.
Select the app target.
Navigate to the General tab.
In the Deployment Info section, select “Scene manifest”.
Add the UIApplicationSceneManifest key if it doesn’t already exist.
Configure the dictionary value for your project.
To provide dynamic scene configurations for complex scenes that require fine-grained control, implement application(_:configurationForConnecting:options:) for UIKit and UIApplicationDelegateAdaptor for apps that uses the SwiftUI life cycle. This allows for providing dynamic scene configurations:
For more information on dynamic configuration, see TN3187: Migrating to the UIKit scene-based life cycle. For more information on adding scene support to your app, see Specifying the scenes your app supports.
Choose the content size and style of your window
After adding scene support, configure the initial size and style of your window’s scenes. When your app creates or restores an instance of your user interface, the system calls scene(_:willConnectTo:options:). This delegate method provides a window scene that you use to configure size contraints and style. For Metal apps and games, this is typically a single scene.
Use UISceneSizeRestrictions to constrain the minimum size you want, and to handle aspect ratio changes:
In SwiftUI, use the windowResizability(_:) modifier to allow your scene’s content to provide sizing information:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.frame(minWidth: 640, minHeight: 360)
}
.windowResizability(.contentMinSize)
}
}To get the display scale, access displayScale from UITraitCollection and perform necessary updates in viewIsAppearing(_:). To calculate the pixel values you use for updating the size of MTLDrawable, multiply the view’s frame and the contentsScale of your CAMetalLayer:
When a person resizes a window, it’s possible that the Metal view only renders to a portion of the window. In this case, add a launch screen with a black background color to letterbox the presentation, then configure the content gravity property for your view so drawable content scales uniformly.
[Image]
Handle window resizing
When resizing a window, the system sets isInteractivelyResizing and calls the scene delegate windowScene(_:didUpdateEffectiveGeometry:) to allow for an app to handle window size changes. When a window resizes, continue rendering at the existing render target size until a person stops resizing the window, at which point you can update the new render target size. Don’t query the window size while a person is resizing a window. Instead, track the state in your renderer and then perform the necessary render size update when the person finishes resizing the window. For more information on responding to scene size changes, see TN3187: Migrating to the UIKit scene-based life cycle.
If you use MetalKit, your app receives the mtkView(_:drawableSizeWillChange:) delegate view callback:
Your CAMetalLayer views receive a UIView life cycle call to layoutSubviews() and related property updates — contentScaleFactor, frame, and bounds. Use the related properties to update the MTLDrawable size by getting the window scene’s bounds from coordinateSpace and multiplying it by the contentsScale of your CAMetalLayer:
Handle moving a window between displays
In iPad, you use UITraitCollection to assist with providing a flexible windowing environment that allows your app to render and move windows between multiple displays. To eliminate the need to manually register for trait changes, use Automatic trait tracking to observe the values you need from your specific views. In some cases, you might use UIScreen to access a trait that UITraitCollection doesn’t provide, like nativeScale.
When your app’s scene geometry changes — like when moving between screens — the UIWindowSceneDelegate calls the windowScene(_:didUpdateEffectiveGeometry:) method to inspect the window geometry and perform necessary updates:
For more information on supporting multiple displays in iPadOS, see Presenting content on a connected display. For more information on managing your Metal app window in macOS, see Managing your game window for Metal in macOS.
Lock interface orientation for device rotation
Some Metal apps and games might need to lock the interface orientation so the screen geometry remains locked when a person rotates the device. To lock the orientation, call setNeedsUpdateOfPrefersInterfaceOrientationLocked() in your view controller and check whether the interface is already locked with the previousEffectiveGeometry parameter of windowScene(_:didUpdateEffectiveGeometry:):
For more information on locking your app’s orientation, see TN3192: Migrating your iPad app from the deprecated UIRequiresFullScreen key.