Skip to content
FoldReadyGet an audit

SwiftUI and UIKit layouts for iPhone Duo: size classes, split views and reserved regions

Updated · 8 min read · Markdown version

On iPhone Duo, read horizontalSizeClass from the SwiftUI environment or the UIKit trait collection instead of checking device idiom, use NavigationSplitView for the regular-width inner display, and keep custom bars out of the fold and camera reserved regions exposed by the iOS 27.1 ReservedRegion API.

Most iPhone Duo layout bugs come from three habits: branching on device idiom, sizing from the screen instead of the container, and drawing custom chrome where the system reserves space. This guide shows the replacement for each.

1. Branch on size class, not idiom or orientation

struct LibraryView: View {
    @Environment(\.horizontalSizeClass) private var hSize

    var body: some View {
        if hSize == .regular {
            // Inner display, or closed device with plenty of width.
            NavigationSplitView {
                SidebarList()
            } detail: {
                DetailPane()
            }
        } else {
            // Outer display, or the app is a compact slice in multitasking.
            NavigationStack { SidebarList() }
        }
    }
}

In UIKit, read traitCollection.horizontalSizeClass and respond in traitCollectionDidChange or, on iOS 17 and later, with registerForTraitChanges. Never cache the value at launch; it changes when the device opens or closes.

2. Size from the container, never from the screen

// Before: breaks the moment the window is not the full screen.
let width = UIScreen.main.bounds.width

// After: UIKit
let width = view.window?.windowScene?.coordinateSpace.bounds.width ?? view.bounds.width

// After: SwiftUI, only when you truly need a number
GeometryReader { proxy in
    Grid(columns: proxy.size.width > 600 ? 2 : 1)
}

Prefer layout containers such as ViewThatFits, adaptive grids and flexible frames over measured widths. When the device opens, the window resizes live and every measured value becomes stale.

3. Respect the fold and camera reserved regions

iOS 27.1 adds ReservedRegion in SwiftUI and UIViewReservedRegion in UIKit. Standard bars and toolbars avoid these regions automatically. Custom bottom bars, floating action buttons and edge-to-edge canvases must query the regions and lay out around them. Two kinds exist: division for the fold and occlusion for the camera. Treat a non-empty division region as the signal that the device is half folded, and move controls to one half.

4. Handle asymmetric safe areas

Do not assume safeAreaInsets.leading equals safeAreaInsets.trailing on the inner display. Use the individual insets and layout margins rather than a single horizontal padding value.

5. Preserve state through the handoff

When the user opens the device, the same scene continues on the inner display with a new size. Keep view models outside of view identity, avoid re-fetching in onAppear without a guard, and store scroll position and in-progress edits in state that survives a layout change.

6. Test the matrix

Every screen should be checked in the six poses, closed, open and half folded in both orientations, and side by side with another app. The Xcode 27.1 Device Hub simulator has buttons to open, close, fold and rotate, which reach all six poses. Automating this matrix with screenshots per pose is what FoldCheck does.

Questions

Does NavigationSplitView work on iPhone Duo?
Yes. On the regular-width inner display it shows a sidebar and detail column; on the compact outer display it collapses to a stack automatically.
Can I detect the half-folded pose in SwiftUI?
Query the ReservedRegion API for the division kind. A non-empty fold region indicates a folded pose; lay out content on either side of it.

Sources

More iPhone Duo guides