Strike a pose with adaptive layouts on iPhone Duo
Design adaptive iPhone Duo layouts around the hinge, cameras, reserved regions, and two-view arrangements in SwiftUI and UIKit.
TL;DR
- iPhone Duo layouts must account for reserved regions: the hinge is a division region, while cameras are occlusion regions that can affect available space.
- Use displacement patterns to move, resize, or reorganize existing elements only when it keeps important content visible, reachable, and contextual.
- SwiftUI and UIKit can query reserved regions for custom layout, including inactive regions for high-level decisions such as grid structure.
- Use ArrangementView or UIArrangementViewController for two-view split or overlay layouts, but do not treat arrangements as navigation containers or place them inside scrollable containers.
Reserved regions define the layout problem
iPhone Duo has multiple displays, each with its own size class, plus hardware features that affect usable space. The hinge and cameras are exposed as reserved regions, similar in spirit to other system-reserved areas your layouts already adapt around.
The outer display camera is always present and system bars lay out within the new safe area. On the inner display, apps may need to adapt around the fold or the FaceTime camera when it is active.
- The hinge is modeled as a division region because it divides one larger area into smaller usable regions.
- The FaceTime camera is modeled as an occlusion region because it covers a smaller area within the view bounds.
- Reserved regions can be active or inactive; the fold division region is active only while the device is folded and has zero width when flat.
Use displacement when natural flow is not enough
Many interfaces can flow naturally around reserved regions. Others benefit from displacement: adjusting the frame of existing elements so important content remains visible, reachable, and unobstructed as the device folds.
Displacement should preserve meaning and relationships. Move a single element when it can adapt independently; move a group together when the elements function as a unit.
- Avoid excessive movement that disconnects an element from its source or context.
- Do not displace continuous scrolling content such as articles, feeds, documents, and lists; scrolling already provides continuity.
- Choose destination based on device posture: alerts may move to the trailing region in book-like use, content may move to the upper region when propped up, and controls may move to the lower region for touch stability.
- When multiple regions are suitable, keep content contextual, such as keeping search positioned over the view it searches.
Query reserved regions for custom layout
For custom views, query reserved regions and incorporate their frames into your own layout. In SwiftUI, use a GeometryProxy from GeometryReader or onGeometryChange. In UIKit, query from UIView.
Inactive regions are useful for decisions that should consider hardware structure even when the region is not currently active, such as preferring an even number of grid columns when a division region exists.
- Query division regions to adapt around the fold.
- Pass includeInactive when inactive regions should influence layout decisions.
- Query occlusion regions to avoid camera areas when they are relevant to the experience.
Query division regions in SwiftUI
Gets active division reserved regions, such as the fold when iPhone Duo is partially folded.
GeometryReader { proxy in
let regions = proxy.reservedRegions(
kind: .division)
}Query reserved regions in UIKit
Reads division regions from a UIView and maps them to frames for use in a custom layout.
let regions = view.reservedRegions(
kind: .division)
let frames = regions.map(\.frame)Include inactive or occlusion regions
Includes inactive division regions for high-level layout choices and queries occlusion regions such as the FaceTime camera.
GeometryReader { proxy in
let divisionRegions = proxy.reservedRegions(
kind: .division,
options: .includeInactive)
let cameraRegions = proxy.reservedRegions(
kind: .occlusion)
}System containers and arrangements
System containers already provide significant adaptation. NavigationStack, NavigationSplitView, TabView, List, and ScrollView adapt to the fold for common navigation and content patterns.
For layouts that sit between navigation and content, iOS 27.1 introduces system-provided arrangements. An arrangement takes inputs such as size classes, aspect ratio, and active division regions, then determines outputs such as which views are shown and what frames they receive.
- Use ArrangementView in SwiftUI for a primary and secondary view inside a navigation container.
- Use UIArrangementViewController in UIKit, commonly as the root view controller of a UINavigationController.
- Use arrangements for two-view layout behavior, not for navigation infrastructure.
Add an ArrangementView in SwiftUI
Places a two-view arrangement inside a NavigationStack with primary and secondary content.
var body: some View {
NavigationStack {
ArrangementView {
PlayerView()
} secondary: {
UpNextView()
}
}
}Add a UIArrangementViewController in UIKit
Creates the UIKit equivalent and assigns primary and secondary view controllers.
let arrangementVC = UIArrangementViewController()
let navController = UINavigationController(rootViewController: arrangementVC)
let playerVC = PlayerViewController()
arrangementVC.setViewController(playerVC, for: .primary)
let upNextVC = UpNextViewController()
arrangementVC.setViewController(upNextVC, for: .secondary)Choose split or overlay arrangements deliberately
The split arrangement divides its bounds between the primary and secondary views. By default it splits horizontally when wider than tall and vertically when taller, and can be restricted to one axis.
The overlay arrangement prefers placing content above or below, and can move to side-by-side placement when the device folds. It is appropriate when one view clearly functions as foreground content over background content.
- Choose split for HStack/VStack-like layouts or main/detail relationships where neither view should be obscured.
- Choose overlay for ZStack-like layouts or foreground/background relationships where partial obscuration is acceptable.
- Use overlayArrangementZIndex in SwiftUI, or arrangement state in UIKit, when a view should change presentation as its overlay ordering changes.
- Avoid putting NavigationSplitView or other navigation containers inside an ArrangementView, and avoid putting ArrangementView inside List or ScrollView.
Configure a horizontal-only split arrangement
Restricts split behavior to the horizontal axis; if the arrangement cannot split on its primary axis, it shows a single view.
var body: some View {
NavigationStack {
ArrangementView {
PlayerView()
} secondary: {
UpNextView()
}
.arrangementViewStyle(
.split.axes(.horizontal))
}
}Use overlay arrangement and react to Z index
Adapts a secondary view between collapsed and expanded states as overlay ordering changes while the device folds or unfolds.
enum UpNextMinimization {
case collapsed
case expanded
}
struct UpNextView: View {
@Environment(\.overlayArrangementZIndex)
private var zIndex: Int
var body: some View {
UpNextList(minimization: minimization)
}
var minimization: UpNextMinimization {
zIndex > 0 ? .collapsed : .expanded
}
}Related Sessions
Raise the bar with iPhone Duo
Adapt SwiftUI and UIKit navigation, toolbar, and tab bar layouts for iPhone Duo's vertical shared bar region, overflow, and custom item behavior.
Design for iPhone Duo
Design iPhone Duo apps that adapt across outer and inner displays, folding poses, side controls, sheets, and fold avoidance.