Skip to content
Crease
Esc
↑↓navigate↵open⌘Jpreview
On this page

iPhone Duo

What Crease needs on iOS, how the data maps, and how to test with the Xcode simulator.

Requirements

The hinge and reserved-region APIs exist in iOS 27.1 and later. Crease needs that version at runtime, on a device that has a hinge. On every other iPhone and on older systems the observer does nothing and rememberFoldState() returns FoldState.None.

The library does not need Xcode 27.1 to build. It looks the APIs up at run time and never names an iOS 27 type, so an app that depends on Crease builds and runs with older Xcode versions too. You only need the newer SDK to run on a simulated or real iPhone Duo.

There is nothing to configure. No Info.plist keys, no entitlements.

What iOS 27.1 added

iOS 27.1 gave UIKit two things for the iPhone Duo that Crease reads:

  • UIHingeInteraction, which reports the hinge status and its angle.
  • Reserved regions of a view, requested by kind with reservedRegionsOfKind:options:. The division kind is the fold, and it is active only while the device is partly folded. The occlusion kind covers things like the inner camera.

Compose Multiplatform itself does not expose these yet. Its iPhone Duo work is tracked in CMP-10776, still open on 2026-10-24.

Using UIHingeInteraction from Kotlin

You don’t call it yourself. Crease owns the interaction and the reserved-region lookups in a small Objective-C class compiled into the library, so the app has no Swift or Objective-C to write and nothing to register at launch. Shared Kotlin code reads the result:

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import com.jantursky.crease.rememberFoldState
import com.jantursky.crease.rememberHinge

@Composable
fun HingeLabel() {
    val state = rememberFoldState()
    val angle = rememberHinge()?.angle
    Text("${state.posture}, ${angle ?: "no angle"}")
}

The same two calls work on Android, where the data comes from Jetpack WindowManager and the hinge-angle sensor.

How the data maps

  • Hinge. Crease owns a UIHingeInteraction and reports its status as Closed, PartiallyOpen or FullyOpen. iOS gives the angle in radians, with 0 shut and π flat. Crease converts it to degrees, so you see 0 to 180 on both platforms.
  • Fold. The division region of the screen becomes a Fold. Points are converted to pixels with the screen scale. Crease publishes it as a fold only while the hinge reads PartiallyOpen.
  • Cutouts. Occlusion regions, such as the camera, become FoldState.cutouts.

Things that behave in a way you might not expect

  • The division region lags the hinge. After a hinge update, UIKit takes a moment to update the division. Crease keeps reading the region for up to two seconds, until the region’s active state agrees with the hinge. A layout that reacts to the fold may therefore settle a little after the angle.
  • An inactive division keeps a 40 pt frame. The region exists even when the device is open flat. Crease ignores it then, so a flat iPhone Duo reports no fold.
  • The angle is the physical hinge, not a layout. Use the posture for layout, see postures and the hinge.

Testing with the simulator

Xcode 27.1 includes an iPhone Duo simulator in the Device Hub. Run your app on it and use the device controls to:

  1. Open and close the device, which moves the hinge between FullyOpen and Closed.
  2. Fold it to a half-open angle, which gives PartiallyOpen, an angle and an active division.
  3. Rotate it, which turns a vertical fold into a horizontal one and a book posture into a tabletop.

Watch rememberFoldState() while you do. If the posture changes the way you expect, your layout works. Before 27.1 is on your machine, use ProvideFoldState to cover the same cases in previews and tests.

Not covered

Crease does not wrap UIArrangementViewController, the UIKit counterpart of SwiftUI’s ArrangementView (iOS 27.1, with UISplitArrangement and UIOverlayArrangement). FoldAwareTwoPane covers a similar need. It does not wrap toolbarVerticalEdge either, which could not be found in the iOS 27.1 UIKit headers or the SwiftUI interface. Outer display scenes are covered by the separate crease-cover module, see cover display.

The iOS 27.1 SDK has two kinds of UISceneAccessory. externalNonInteractive is for a connected external display. cameraCapture shows its scene only while the app is in the foreground with an active camera capture session, and the system decides whether and where to present it. So on the iPhone Duo the outer display is, as far as the SDK shows, for camera apps: an app that is not capturing has nothing to put there. This is read from the headers, not tried on a device or the simulator. Two Feedback Assistant reports on the community tracker (FB25262170 for UIKit, FB25262179 for SwiftUI) ask Apple to allow non-camera accessories there. Their current status is not checked here.

Last updated on

Was this page helpful?