The first question I heard from colleagues after the iPhone Duo announcement: "can you read the fold angle?" You can. Apple exposes both a discrete hinge status and a continuous angle — in SwiftUI and in UIKit. But the same Tech Talk that demos this API delivers a hard restriction: the hinge is for effects and interactions, never for layout. The distinction is fundamental, and the entire second half of the new APIs is built on it. I'll go through everything in order: hinge APIs, reserved regions, arrangements and cameras — noting what's callable from Objective-C and where you'll need Swift.
As in the first article of the series, a reminder: signatures come from Tech Talks 111463–111465, Xcode 27.1 is still in beta — check against the SDK headers before shipping.
onHingeChange: three statuses and a live angle#
The SwiftUI modifier onHingeChange calls a closure with the previous and current context. Apple's canonical example is a guitar app where the fold angle works as a tremolo arm:
struct InstrumentView: View {
/// 0 — no bend, 1 — maximum
@State private var pitchBend: Double = 0
var body: some View {
GuitarView(pitchBend: pitchBend)
.onHingeChange { _, context in
if let hinge = context.hinge, hinge.status == .partiallyOpen {
pitchBend = calculatePitchBend(angle: hinge.angle)
} else {
pitchBend = 0
}
}
}
}Two mandatory pieces here are easy to miss:
context.hingeis Optional.nilmeans the device has no hinge — the same binary runs on regular iPhones. Without the guard, this code fails on most devices in the world.- The
elsebranch that resets. There are three statuses:.closed,.partiallyOpen,.fullyOpen. If you reset the effect only on.closed, opening the device flat leaves the effect stuck at the last angle.
In UIKit the same job is done by UIHingeInteraction — following the UIInteraction family convention, it attaches to a view, which also means it's reachable from Objective-C:
// API shape follows the UIInteraction family; check the exact
// handler signature against the iOS 27.1 SDK headers
UIHingeInteraction *hinge = [[UIHingeInteraction alloc] init];
[self.effectView addInteraction:hinge];What's notably absent here: notifications. Not a single official source names an NSNotification for the hinge — observation goes through the interaction/modifier only. If you run into UIDeviceHingeDidChangeNotification somewhere, it's made up.
Why the hinge is not for layout#
The temptation is obvious: read the angle and lay the interface out based on it. Apple says in plain words not to. The angle is a continuous sensor stream; layout tied to it jitters while the device is folding and breaks on every hinge-less device. For structural decisions there are two separate APIs.
Reserved regions (iOS 27.1) are areas of a view "claimed" by hardware. Two kinds: .division — the fold, splitting the area (active only while the device is partially folded; when flat, the region is inactive and has zero width); .occlusion — cameras, covering the area.
// SwiftUI — from GeometryProxy
let folds = proxy.reservedRegions(kind: .division)
let cameras = proxy.reservedRegions(kind: .occlusion)
// For stable decisions: account for the fold even while it's inactive
let allFolds = proxy.reservedRegions(kind: .division, options: .includeInactive)// Objective-C: same method on UIView, regions are UIViewReservedRegion
NSArray<UIViewReservedRegion *> *folds =
[self.view reservedRegionsWithKind:UIViewReservedRegionKindDivision
options:0];
for (UIViewReservedRegion *region in folds) {
// region.frame — the fold rectangle in view coordinates
}The .includeInactive option solves a non-obvious problem: if your grid rebuilds every time the fold region appears and disappears, the interface reshuffles with every hinge movement. A stable decision looks like, say, always using an even number of columns on any device that has a fold at all.
Good news: system containers ship fold avoidance for free. NavigationSplitView, TabView, sheets, alerts, menus and popovers move interactive elements away from the fold's curvature on their own. The single exception is scrollable content: an article or a feed is allowed to flow under the fold — no need to move it.
ArrangementView: split or overlay#
For a pair of views "between navigation and content" there's now a dedicated container. In SwiftUI it's ArrangementView, in UIKit — UIArrangementViewController:
ArrangementView {
PlayerView()
} secondary: {
UpNextView()
}
.arrangementViewStyle(.split)UIArrangementViewController *arrangement = [[UIArrangementViewController alloc] init];
[arrangement setViewController:playerVC forPlacement:UIArrangementPlacementPrimary];
[arrangement setViewController:upNextVC forPlacement:UIArrangementPlacementSecondary];Two styles with different semantics. Split divides the bounds and overlaps nothing — main/detail, like a player with a transcript; a pleasant bonus over the iPad behavior: when the secondary closes, the primary view doesn't re-center across the whole screen, it stays by the fold. Overlay is a stack that lays out side by side when the device bends; that's foreground controls over scrollable content. The migration heuristic is simple: whatever used to be an HStack/VStack becomes split; whatever used to be a ZStack becomes overlay.
And two prohibitions from the Tech Talk: don't put a NavigationSplitView inside an arrangement (the container provides no navigation infrastructure) and don't put an arrangement inside a List/ScrollView. If you need collapsing columns, that's still UISplitViewController — arrangement is for something else.
Cameras: position == .front no longer means "facing you"#
The Duo is the first iPhone with two front cameras: an outer one (4K/120) and an inner under-display one (1080p/60). Both honestly report position == .front — yet the displays can face opposite directions, so a "front" camera doesn't necessarily face the user. Every piece of mirroring logic written before 2026 breaks on this fact.
The easy path is the virtual front camera: a system device that switches between the inner and outer module by itself as the device opens and closes. Discovery works as before (AVCaptureDeviceDiscoverySession with position .front — works from Objective-C too), but what it exposes is only the intersection of both cameras' capabilities: 1080p, 60 fps, no depth. For most apps that's plenty, and module switching stops being your problem.
If you need the full capabilities of a specific module, there are new types .builtInInnerUltraWideCamera and .builtInOuterUltraWideCamera — but then switching is on you, and this is where the main new tool comes in — AVCaptureDeviceDirectionCoordinator from AVKit:
let coordinator = AVCaptureDeviceDirectionCoordinator(
view: previewView,
deviceTypes: [.builtInInnerUltraWideCamera, .builtInOuterUltraWideCamera]
) { descriptor in
// The descriptor is Sendable; build the AVCaptureDevice on the camera actor
Task { await cameraActor.switchTo(descriptor) }
}The coordinator is tied to a specific view and answers the question "which camera currently faces the same direction as this display". It's isolated to the main actor, and what crosses the actor boundary is not an AVCaptureDevice but an AVCaptureDeviceDescriptor — a sendable representation of the device. The change handler has three duties: reconfigure the session, recompute mirroring by direction, not by position, and update the UI.
Two more camera details from Tech Talk 111465: after moving to AVCaptureDeviceRotationCoordinator, turn off isCameraSensorOrientationCompensationEnabled — on the Duo's front cameras the compensation is enabled by default and would become double work; and dynamicAspectRatio lets you use the square sensor for the inner display's landscape.
A teleprompter on the second display#
Camera apps get one more capability: while the main UI occupies the inner display with an active capture session, the outer one can show extra content — CameraCaptureAccessory. The talk's scenarios: a teleprompter for video recording, or something fun for a kid who's being photographed.
CameraView(model: model)
.sceneAccessory {
CameraCaptureAccessory(isEnabled: $model.isEnabled) {
TeleprompterView(model: model)
}
.onAvailabilityChange { model.isAvailable = $0 }
}Availability is controlled by the system, so onAvailabilityChange is a duty rather than an option: the accessory can vanish at any moment the user folds the device.
The big picture#
The logic of the new APIs adds up to a coherent whole: the hinge is a sensor for effects, reserved regions are hardware facts for layout, arrangements are ready-made patterns for a pair of views, and camera direction is its own entity, not derivable from position. The entire UIKit/AVFoundation surface is reachable from Objective-C; the SwiftUI exclusives (onHingeChange, ArrangementView, sceneAccessory) will require either UIKit counterparts or a thin Swift shim in an ObjC project — that's what the third article of the series is about.



