Folding or unfolding iPhone Duo doesn’t relaunch your app. The scene stays connected and your process keeps running: the system just resizes the window and changes its traits. Any code that read the screen size once, or that guesses the layout from the device, keeps using stale numbers. That is where stretched, clipped and misplaced content comes from.2
Start with a project-wide search for each pattern below.
1. UIScreen.main
On a device with two displays, “the main screen” is ambiguous. Apple says the property will be deprecated in a future release.12
| Instead of | Use |
|---|---|
UIScreen.main.bounds |
view.bounds or window.bounds; in SwiftUI, GeometryReader or onGeometryChange |
UIScreen.main.scale |
traitCollection.displayScale; in SwiftUI, @Environment(\.displayScale) |
UIWindow(frame: UIScreen.main.bounds) |
UIWindow(windowScene:) |
A stored UIScreen |
view.window?.windowScene?.screen, read when you need it |
func updateThumbnail(from image: UIImage) {
// Before
let screenScale = UIScreen.main.scale
// After
let screenScale = traitCollection.displayScale
// ...
}
2. Reading the size once
Reading a size at launch or in viewIsAppearing isn’t enough, because a fold can resize the app in the middle of a session.2 Do size-dependent layout where UIKit calls you on every change:
layoutSubviewsorviewDidLayoutSubviewsfor layout.viewWillTransition(to:with:)for work that has to happen when the size changes, such as reloading a collection view’s layout.
In SwiftUI, onGeometryChange gives you the same thing without wrapping your view in a GeometryReader.
3. Orientation as a layout signal
On iPhone Duo the device’s orientation doesn’t tell you your app’s shape. The inner display doesn’t honor your supported interface orientations at all, and Split View can make a “landscape” device host a tall, narrow app.1 Remove layout logic that reads:
UIDevice.current.orientationstatusBarOrientationinterfaceOrientation, includingwindowScene.effectiveGeometry.interfaceOrientation
If you need to know whether you’re wide or tall, compare the space you actually have:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let isWide = view.bounds.width > view.bounds.height
// Update layout here
}
Use superview.bounds inside a view, or the size from GeometryReader in SwiftUI.
Orientation locking still works on the outer display, which honors UISupportedInterfaceOrientations like any iPhone. A portrait-locked app still receives compact and regular size class changes on the inner display, including in Split View, so it still needs to adapt.3
4. Idiom checks
Search for userInterfaceIdiom == .phone and == .pad. Between iPhone Duo and resizable iPhone Mirroring on the Mac, an iPhone app can now receive any combination of size classes. Code that shows an iPad-only layout when horizontalSizeClass == .regular, or assumes an iPhone is always compact, will run that layout on an iPhone. Base the decision on size classes or available space instead.2 See Size classes, safe areas and standard containers.
5. Known device sizes
Comparisons like bounds.height == 844, or a table of known iPhone screen sizes, won’t match iPhone Duo or any of its poses. Delete them.
Bonus: full-bleed media
Hero images and video using .scaleAspectFill or .aspectRatio(contentMode: .fill) can crop important content on the much wider inner display. Choose fill or fit from the current size class or aspect ratio, or give the image a focal point.1
If you’re stuck on frame-based layout
Apple’s engineers recommend Auto Layout with translatesAutoresizingMaskIntoConstraints = false over manual frames. If a screen genuinely needs manual frames, compute them from view.bounds and the safe area in viewDidLayoutSubviews, never from cached values.3
UIRequiresFullScreen is not an escape hatch
iPhone Duo still honors UIRequiresFullScreen, but your app is resized anyway when the device opens or closes. The key is deprecated: Apple points to TN3192 for migrating off it, and recommends removing it as soon as you can.3