WWDC.ai

Leverage multiple displays and scenes on iPhone Duo

Build iPhone Duo experiences that respond to hinge angle, support side-by-side multitasking and multiple scenes, and span displays with scene accessories.

Watch on Apple Developer

TL;DR

  • Use SwiftUI's onHingeChange or UIKit's UIHingeInteraction to observe hinge status and continuous angle updates for live interactions and effects.
  • Treat hinge data as an interaction signal, not a layout mechanism; use arrangement, region, size class, and scene geometry APIs for layout decisions.
  • All apps participate in iPhone Duo multitasking, and iPhone Duo supports multiple instances of an app's UI, with new-window creation dynamically unavailable on the outer display.
  • Use scene accessories, including CameraCaptureAccessory, to pair supplementary UI with a main scene across displays and observe accessory availability changes.

Respond to the hinge

iPhone Duo exposes hinge information so apps can react to the device being closed, partially open, or fully open. SwiftUI uses the onHingeChange modifier, while UIKit uses UIHingeInteraction. Both provide high-level status and continuous hinge angle updates.

The session's example uses the hinge angle to drive a guitar pitch bend. The implementation keeps the effect in view state, checks that a hinge exists, limits the effect to the partially open state, and resets the state when hinge data should not be used.

  • A nil hinge means the app is running on a device without a hinge.
  • Use hinge.status == .partiallyOpen when an interaction only makes sense while the device is being flexed.
  • Reset interaction state when the hinge is unavailable or not in the expected state.

Drive an effect from hinge angle in SwiftUI

Observe the current hinge context, guard for hinge availability and state, then map hinge.angle into app-specific state.

struct InstrumentView: View {
    /// Normalized bend, 0 is no bend, 1 is deepest bend
    @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
                }
            }
    }

    private func calculatePitchBend(angle: Angle) -> Double { ... }
}

Use layout APIs for layout, not hinge angle

Hinge observations are live input suitable for effects, gestures, and other interactive behavior. Layout should instead be driven by the platform's adaptive layout APIs.

For iPhone Duo layouts, use arrangement and region APIs, along with familiar tools such as size classes and scene geometry. The related session "Strike a pose with adaptive layouts on iPhone Duo" covers those APIs in more detail.

  • Use hinge angle for continuous effects such as zooms, bends, or other direct feedback.
  • Use arrangement and region APIs when deciding where UI belongs around the fold or across displays.
  • Use size classes and scene geometry for multitasking and dynamically resized windows.

Prepare for split view multitasking and multiple scenes

All apps participate in multitasking on iPhone Duo, where two apps can be placed side by side. iPhone Duo also introduces a layout that stacks video and apps together; apps should handle these layouts through the same adaptive layout techniques.

iPhone Duo is the first iPhone to support multiple instances of an app's UI. Apps that already support multiple windows on iPad also support this on iPhone Duo, but there is a platform-specific constraint: new windows cannot be created on the outer display. New window creation is reserved for the inner display.

  • If the app already resizes well on iPad or iPhone mirroring, it has a strong starting point for iPhone Duo multitasking.
  • Handle errors when requesting new scenes because availability can change dynamically.
  • Use the UIWindowSceneActivation action for user-facing new-window UI; it automatically hides when new windows are unavailable.
  • Consult multiple-scene documentation if adding multiwindow support for the first time.

Span displays with scene accessories

Scene accessories let an app pair supplementary content with its main UI on another display. The session frames this as a way to show app-controlled companion UI across displays, such as using one device display as a controller or showing supporting content while the main scene remains elsewhere.

Accessory availability is controlled by the system. Accessories are enabled by default, can be toggled, and may become unavailable as conditions change. Apps should observe availability so controls and state stay synchronized.

  • Register accessories where they are relevant in the view hierarchy so the accessory appears only while that UI is visible.
  • Use an enabled binding when the user should be able to toggle the accessory.
  • Use onAvailabilityChange to update app state when the accessory can no longer be shown.

Register a camera capture accessory

Attach the accessory to the camera UI so the teleprompter appears on the outer display only while the camera view is visible.

struct CameraRootView: View {
    @State private var model = TeleprompterModel()

    var body: some View {
        CameraView(model: model)
            .sceneAccessory {
                CameraCaptureAccessory {
                    TeleprompterView(model: model)
                }
            }
    }
}

Toggle the accessory and react to availability

Bind accessory enablement to model state and disable the toolbar toggle when the system reports the accessory is unavailable.

struct CameraRootView: View {
    @State private var model = TeleprompterModel()

    var body: some View {
        CameraView(model: model)
            .sceneAccessory {
                CameraCaptureAccessory(isEnabled: $model.isEnabled) {
                    TeleprompterView(model: model)
                }
                .onAvailabilityChange { newValue in
                    model.isAvailable = newValue
                }
            }
            .toolbar {
                TeleprompterToggle(isEnabled: $model.isEnabled)
                    .disabled(!model.isAvailable)
            }
    }
}

CameraCaptureAccessory for camera apps

CameraCaptureAccessory is a scene accessory for camera apps on iPhone Duo. It lets the app keep its main camera UI on the inner display while showing additional UI on the outer display, such as a teleprompter for the person being recorded.

The accessory is available when the app is full screen on the inner display and has an active camera session. Register it on the same view as the camera UI.

  • Use it for camera-related supplementary UI shown to the subject while capturing photo or video.
  • Expect availability to change, for example when the device is closed.
  • For deeper camera-specific behavior, consult the related session "Build a great camera experience for iPhone Duo".
Unofficial, not associated with Apple. Made with ❤️ by

On this page

Ask AI