Layout

Size from the window, not the screen

Replace UIScreen.main, orientation checks, idiom checks and hard-coded device sizes so your iOS app resizes correctly when iPhone Duo folds and unfolds.

7 min readUpdated 3 sources

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:

  • layoutSubviews or viewDidLayoutSubviews for 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.orientation
  • statusBarOrientation
  • interfaceOrientation, including windowScene.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

Sources

  1. Apple Developer: Prepare your app for iPhone Duo (Step 2: Fix patterns)
  2. Tech Talk: Prepare your app for iPhone Duo (Avoid screen assumptions)
  3. Apple Developer Forums: A summary of the iPhone Duo Group Lab