Skip to content
Mobile
Skill

/iphone-duo-adaptive-layout

Build layouts that adapt to the iPhone Duo fold using reserved regions, division and occlusion regions, displacement patterns, and the split and overlay ArrangementView containers. Use when content or controls land in the fold, when building a split-like or overlay layout, or

From plugin
iphone-duo-skills
31011 skills

Claude Code

$ npx -y skills add mirzaaghazadeh/iphone-duo-skills --skill iphone-duo-adaptive-layout --agent claude-code

SKILL.md

iphone-duo-adaptive-layout.SKILL.md
name: iphone-duo-adaptive-layout
description: Build layouts that adapt to the iPhone Duo fold using reserved regions, division and occlusion regions, displacement patterns, and the split and overlay ArrangementView containers. Use when content or controls land in the fold, when building a split-like or overlay layout, or when custom manually-positioned UI must avoid the hinge or the under-display camera.

Adaptive layouts around the fold

When iPhone Duo is partially folded the inner display curves through the middle. Anything sitting in that curve gets harder to read and much harder to tap. The same thing happens to a photo printed across the spine of a book: near the gutter it stops reading as one continuous image.

So the fold is not decoration. It is a real constraint on where content can go — and iOS models it as a **reserved region** you can query and design around.

Reserved regions

Reserved regions are areas of the view that hardware imposes on your layout. Treat them exactly like the window controls you already work around on iPadOS: not obstacles, just facts about the available space.

Two kinds, and the distinction matters:

**Division regions** split one area into several smaller usable areas. The fold is a division region. Content does not flow *through* it — it belongs on one side or the other.

**Occlusion regions** cover part of an area without splitting it. Think of them as frames sitting inside your bounds. The inner FaceTime camera is an occlusion region.

Concretely, iPhone Duo has **three** reserved regions, and it's worth knowing which is which because they behave differently:

| Region | Kind | When it's there | |---|---|---| | Outer front camera | occlusion | **Always.** Sits in the corner, aligned with the side controls, and expands into the Dynamic Island for Live Activities. | | Inner front camera | occlusion | Only while that camera is active. It's under the display and invisible otherwise; when it activates, UI moves aside to reveal it. | | The folding region | division | Only while the device is partially folded, dividing the inner display and excluding the curved centre. |

Regions are **active or inactive**:

  • The fold's division region is active only while the device is partially

folded; fully open, it is inactive. The Tech Talk adds that it then has zero width, but the later reference and Group Lab Day 3 both say an inactive region keeps a frame, and suggest reading it ahead of time so a layout already clears the fold and nothing moves when the device bends. Check that frame in the simulator before depending on its size.

  • The camera's occlusion region is active only while that camera is streaming.

Note the asymmetry between the two cameras: the outer one is a permanent fact of your layout, while the inner one appears and disappears under you. Handle the second as a change you animate through, not a constant you read once.

You get only the active ones by default; the Tech Talks and the API reference agree on that. Pass the `includeInactive` option to also see regions that exist but aren't currently in play — which is what you want for stable, high-level decisions. A grid, for instance, can prefer an even column count on any device that *has* a fold, so the layout doesn't reshuffle every time the user bends the device.

Querying them

In SwiftUI, get a `GeometryProxy` from `GeometryReader` or `onGeometryChange`, then ask it for reserved regions. Note the two signatures differ — SwiftUI takes a third parameter UIKit doesn't:

// SwiftUI, on GeometryProxy
func reservedRegions(
    kind: ReservedRegion.Kind,
    options: ReservedRegion.QueryOptions,
    layoutDirectionBehavior: LayoutDirectionBehavior
) -> [ReservedRegion]

// UIKit, on UIView
func reservedRegions(
    kind: UIView.ReservedRegion.Kind,
    options: UIView.ReservedRegion.QueryOptions = []
) -> [UIView.ReservedRegion]

The `layoutDirectionBehavior` argument matters in right-to-left layouts: the fold is fixed in hardware, so whether you want region positions mirrored with your content or left in physical coordinates is a decision you have to make rather than inherit.

Pass `.includeInactive` in `options` to also get regions that exist but aren't currently active.

Each region gives you more than a rectangle:

  • **`frame`** — where it sits in the view's coordinate space.
  • **`isActive`** (`active` in Objective-C) — whether it's currently in play.
  • **`kind`** — division or occlusion.
  • **`margins`** — the clearance the system keeps for interactive content. The

reference is explicit that **`frame` already includes these margins**, so keeping controls out of `frame` is enough; don't pad by `margins` a second time. Inset `frame` by `margins` to get the bare hardware area — the limit for full-bleed media, which may run closer than controls.

  • **`id`** — stable across queries, so you can tell the region you saw last time

from a new one. `ReservedRegion` is `Identifiable`, so regions can drive a `ForEach` directly.

Frames are in the coordinate space of the view you queried, with `(0, 0)` at its top-leading corner, and only regions that overlap that view come back. A region that clips a corner can have a negative origin or run past your bounds, so don't assume it lies inside them. Query in the view that does the positioning, so the frames and the content share coordinates. Two places this bites:

  • In a custom SwiftUI `Layout`, the `bounds` passed to `placeSubviews` don't

always start at `(0, 0)`. Compute positions from zero and add `bounds.origin` only when you place each subview.

  • In UIKit, read regions in `updateConstraints()`. UIKit tracks what you read

there and calls it again when the regions change, so you don't need an observer of your own.

In SwiftUI the query takes a third parameter, `layoutDirectionBehavior` (default `.mirrors`), which flips region frames for right-to-left layouts so a custom `Layout` doesn't have to. Pass `.

Read more
@mirzaaghazadeh
Ships withiphone-duo-skills

Image © Apple Inc., from the iPhone Duo developer page, used for identification. Not affiliated with or endorsed by Apple. Agent skills for building iOS apps on iPhone Duo — Apple's first foldable iPhone, announced September 9, 2026.

Stars on GitHub

Momentum

stars in total
310
Star history of mirzaaghazadeh/iphone-duo-skills on GitHub, a chart by star-history.com

Other skills on iphone-duo-skills.

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo5d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens3w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about iphone-duo-adaptive-layout.

Which coding agents can use iphone-duo-adaptive-layout?
iphone-duo-adaptive-layout installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.