Skip to content
iPhone Duo SupportGet help

Unity games on iPhone Duo: resolution changes mid-play

Ingenious Techlab Team

Engineering analysis, not vendor guidance

Unity has not shipped iPhone Duo support or published guidance for it. Everything below is our analysis of how Unity behaves against the APIs and behaviours Apple has documented. Treat the Apple-side facts as authoritative and the Unity-side conclusions as reasoned inference to verify against your own app.

Unity has shipped no iPhone Duo support. This is our analysis of how a Unity iOS build behaves against the APIs and behaviours Apple documented on 9 September 2026.

Games have a harder problem than apps here. An app is built from views that expect to be resized. A game usually renders to a surface whose dimensions were read once at startup and treated as fixed for the session. On iPhone Duo, they are not.

Resolution changes while the game is running

Screen.width and Screen.height now change mid-session: when the device is folded or unfolded, when the app moves between the inner and outer displays, and when Split View is resized by the user.

Anything cached in Start() is stale from that moment. That typically includes camera viewport rects, canvas scaler reference resolutions, render textures, and any world-to-screen maths.

int _lastW, _lastH;

void Start() { _lastW = Screen.width; _lastH = Screen.height; }

void Update() {
    if (Screen.width != _lastW || Screen.height != _lastH) {
        _lastW = Screen.width;
        _lastH = Screen.height;
        OnResolutionChanged();
    }
}

void OnResolutionChanged() {
    // Rebuild render targets, recompute viewport rects, refresh UI anchors
}

Polling is unglamorous but reliable, and the check is cheap. The alternative — assuming a resolution change implies a scene reload — does not hold here, because the player can fold the device mid-level.

Render textures are the expensive case. Reallocating every frame during a fold animation will hitch, so debounce: wait for dimensions to settle for a few frames before reallocating.

Safe area

Screen.safeArea is the right API and it works correctly. The trap is the same one native apps hit:

// Wrong — asserts the two sides match
float margin = Screen.safeArea.x;
rect.offsetMin = new Vector2(margin, rect.offsetMin.y);
rect.offsetMax = new Vector2(-margin, rect.offsetMax.y);

// Right — use the rect as given
Rect safe = Screen.safeArea;
rect.anchorMin = new Vector2(safe.x / Screen.width, safe.y / Screen.height);
rect.anchorMax = new Vector2((safe.x + safe.width) / Screen.width,
                             (safe.y + safe.height) / Screen.height);

Apple states safe areas are asymmetric on iPhone Duo, so any single-margin derivation is wrong on at least one edge.

Note that Screen.safeArea must be re-read after every resolution change, alongside everything else.

Aspect ratio is the design decision

The inner display is roughly 1.42. Most mobile games are tuned for something near 2.17. That gap is large enough to be a design decision rather than a scaling parameter.

Two honest options, and they should be chosen per scene:

Letterbox to a fixed aspect. Predictable, safe, and preserves your composition exactly. On a 1.42 display it leaves substantial bars — around a fifth of the screen for 16:9 content. For a game with hand-authored levels tuned to a specific frame, this is often correct.

Extend the camera frustum. Fills the display, but shows more of the world than the level was designed to reveal. For a 2D platformer this can expose off-screen enemies or unbuilt geometry. For a 3D game with a full environment it is usually free.

The version to avoid is scaling to fill with a fixed aspect, which crops around a quarter of the frame width. In a game where the edges carry information — a HUD, an enemy indicator, a minimap — that is a functional regression, not a cosmetic one.

The hinge

Division regions have no Unity binding. Reaching them means a native plugin wrapping reservedRegions(kind:) and marshalling frames into C#.

That is real work, and for most games there is a cheaper mitigation that captures most of the benefit: keep critical HUD elements out of the horizontal centre band when running on a foldable. You cannot know exactly where the fold is without the native API, but you know roughly where it is, and moving a health bar away from centre is a five-minute change.

For a game where the fold is genuinely part of the experience — a board game across the crease, a two-player mode with the device propped — the plugin is worth building. For a runner or a puzzle game, it is not.

Lifecycle: the freeze bug

This is the failure most likely to reach players.

Folding the device can move your app between displays without backgrounding it, and in Split View your game can be visible but not frontmost. A common Unity pattern is:

void OnApplicationPause(bool paused) {
    Time.timeScale = paused ? 0f : 1f;
}

On iPhone Duo that can leave a visibly frozen game on screen. Test folding and unfolding during active play, not from a paused menu — and consider distinguishing “not frontmost but visible” from “backgrounded” before stopping simulation.

Input

Touch coordinates are reported in the current screen space, so they follow resolution changes automatically. What does not follow is any input region computed from cached dimensions — virtual joystick bounds, tap zones, swipe thresholds expressed in pixels. Recompute these in OnResolutionChanged along with everything else.

A pragmatic checklist

  1. Add a resolution-change detector and route all screen-derived state through it
  2. Re-read Screen.safeArea on that event; use the rect, not a single margin
  3. Decide letterbox versus extended frustum, per scene, deliberately
  4. Test folding during active play and fix any pause-driven freeze
  5. Recompute input regions on resolution change
  6. Move critical HUD out of the centre band
  7. Consider a native plugin only if the fold is part of the design

Items 1 and 4 are the ones players will notice.

Related

Want this done for you?

We add iPhone Duo and adaptive-layout support to existing iOS apps, native and cross-platform.