Skip to content

iOS player chrome changes size or position between video aspect ratios #258

Open

Description

@jlipworth

Confirmed symptom / product concern

On iOS, the player chrome controls themselves appear to change size and/or position between videos with different widths/aspect ratios. This is not merely a report that the aspect-fit picture becomes wider/shorter inside a fixed player.

The user is unsure this content-dependent chrome movement is desirable. Player controls should normally occupy a stable screen-relative layout for a fixed device, scene size, orientation, Dynamic Type setting, and chrome state, regardless of the video's presentation aspect.

The earlier framing of this issue treated active-picture resizing as the likely phenomenon. That remains a useful comparison to measure, but chrome movement is the primary report.

Current code trace

There is no intentional source-aspect input to mobile chrome geometry:

  • DetailView presents CustomPlayerView in a .fullScreenCover and ignores safe areas.
  • CustomPlayerView puts PlayerLayerView and CustomPlayerChrome beside each other as siblings in one full-surface black ZStack.
  • The UIKit host's backing layer is the AVPlayerLayer. Fit/Fill changes videoGravity (resizeAspect / resizeAspectFill) and therefore the active picture rectangle, but no videoRect, presentation size, natural size, encoded dimensions, or media aspect is passed to CustomPlayerChrome.
  • CustomPlayerChrome measures its own proposed viewport with onGeometryChange; MobilePlayerChromeLayoutPolicy selects phone-landscape/compact variants from horizontal/vertical size classes, idiom, and that viewport size only.
  • In phone landscape, top utility sides, center play/pause, bottom insets, platter padding, skip buttons, menu minimum widths, and row spacing are fixed by the selected chrome variant.
  • The only GeometryReader uses in the chrome are menu/timeline geometry; neither reads the player layer or active video rectangle.
  • On regular iPad layouts, ViewThatFits can switch header/menu composition based on available horizontal space. That space should come from the scene/window proposal, not the video aspect.

Therefore the current source does not deliberately anchor or size chrome to the active video rectangle. If the controls genuinely move with source aspect, it is an unintended emergent layout/lifecycle effect and needs device evidence rather than dismissal as normal aspect-fit behavior.

Plausible non-aspect inputs to rule out

Several state changes can alter the chrome and may correlate with switching videos:

  • iPhone landscape entry timing, scene geometry, safe-area/status-bar/home-indicator changes;
  • the initial zero/unmeasured chromeViewportSize becoming measured, or crossing a compact-layout boundary on iPad;
  • chrome visibility (statusBarHidden / persistent system overlays) changing system safe-area behavior;
  • conditional PiP availability changing the top-trailing utility group;
  • conditional Quality availability (supportsQualityReload) changing the menu strip;
  • title length and iPad ViewThatFits choosing inline versus stacked headers;
  • transport status, skip marker, Up Next, subtitle, scrub-preview, or open-menu state inserting/removing overlays/rows;
  • a controller/item replacement rebuilding the player/chrome while the orientation or full-screen-cover transition is still settling.

None of these is supposed to scale controls from video width. The investigation must compare like-for-like chrome state so a capability/title/status difference is not mistaken for aspect coupling.

Discovery plan

Use a physical iPhone first, holding device/orientation, Dynamic Type, display mode, backend/session capability, and chrome state constant. Compare representative portrait/phone-shot, 4:3, 16:9, approximately 2:1, and 2.39:1 items.

Capture after layout settles and, separately, during startup/item replacement:

  • outer full-screen/player bounds and safe-area insets;
  • measured chromeViewportSize and selected MobilePlayerChromeLayoutPolicy branch;
  • frames of top utility controls, center play/pause, bottom control platter, timeline, and menu buttons;
  • AVPlayerLayer.bounds and videoRect as an independent comparison;
  • horizontal/vertical size classes, interface orientation, system-overlay visibility, PiP possibility, available menus, and relevant inserted chrome states.

A diagnostic overlay/log should use normalized geometry and labels only; do not record media titles, server URLs, tokens, or other private data.

Product decision

After proving which frames move:

  • If only videoRect changes: keep that as ordinary aspect-preserving Fit/Fill behavior unless a separate stable-picture-envelope design is desired.
  • If chrome frames change while viewport/state are equivalent: treat it as a layout bug and anchor chrome to the stable full-screen/safe-area viewport, not the active picture.
  • If capability/title/status changes cause the apparent shift: decide whether those variations are valid or whether chrome groups need reserved positions/stable envelopes across items.

Do not solve chrome movement by stretching video, silently cropping content, or binding controls to a hard-coded 16:9 frame.

Acceptance criteria

  • Reproduce/document the control movement on a physical iPhone with at least two different source aspects while matching orientation, Fit/Fill selection, chrome visibility, and playback state.
  • Measure chrome frames separately from AVPlayerLayer.videoRect; identify exactly which controls change size/position and by how many points.
  • Identify whether the trigger is source aspect, scene/safe-area transition, a mobile layout-policy branch, conditional control availability, title/content sizing, or player reconstruction timing.
  • For a fixed viewport and equivalent chrome state, top controls, center transport, bottom platter/timeline, subtitles, and status overlays remain screen-relative and stable across source aspects.
  • Control hit targets remain at least 44×44 pt and VoiceOver focus order/labels remain stable.
  • Source display aspect remains correct; no stretch or accidental crop is introduced to stabilize chrome.
  • Verify online and completed Offline playback because both converge on CustomPlayerView; expand across backends only if session capabilities are part of the cause.
  • Audit iPad portrait/landscape plus compact Stage Manager/Split View, where ViewThatFits and compact-policy boundaries can legitimately reflow; do not assume iPhone findings apply unchanged.
  • Regression-check iPhone landscape entry/exit and chrome auto-hide/system-overlay transitions.

Platform boundary

Initial confirmed scope is LabstreamMobile on iPhone. iPadOS shares the player/chrome implementation but has different orientation, size-class, and windowing behavior and is an explicit audit target. visionOS, macOS, and tvOS are not confirmed affected and should not be redesigned without separate evidence.

Relationship to earlier work

Both are closed and neither provides the focused geometry evidence required for this report.

Activity

  1. changed the title [-]iOS player: decide whether the visible video frame should stay stable across aspect ratios[/-] [+]iOS player chrome changes size or position between video aspect ratios[/+] on Jul 23, 2026
  2. jlipworth commented on Jul 23, 2026

    @jlipworth
    OwnerAuthor

    Correction after user clarification

    The primary symptom is chrome-control movement: on iOS, the player controls themselves appear to change size and/or position depending on the video's width/aspect ratio. It is not just the visible video raster changing shape under normal aspect-fit presentation.

    I re-traced current main with that distinction:

    • PlayerLayerView and CustomPlayerChrome are siblings in the same full-screen ZStack.
    • No AVPlayerLayer.videoRect, presentation size, natural size, encoded dimensions, or media aspect is passed into chrome layout.
    • Mobile chrome chooses its variants only from idiom, size classes, and the chrome's measured outer viewport. Phone-landscape control sizes/insets are otherwise fixed.
    • Chrome GeometryReader usage is limited to menu/timeline layout and does not read the active video rectangle.

    So there is no intentional code path that anchors chrome to video width. If the reported controls move with source aspect, that is either an unintended layout/lifecycle effect or a correlated state change (orientation/safe-area settling, PiP/Quality availability, title/menu composition, status/Up Next/subtitle insertion, or player reconstruction). It should not be waved away as standard .resizeAspect behavior.

    The issue title/body have been corrected to make chrome movement primary. The earlier video-rectangle theory is retained only as a comparison measurement: device evidence should log/overlay outer viewport, chrome frames, and videoRect separately while holding chrome state constant.

  3. jlipworth commented on Jul 23, 2026

    @jlipworth
    OwnerAuthor

    Additional reproduction: narrower bottom chrome on one item

    A more specific physical-iPhone observation strengthens this as a chrome-container issue: for most videos, the bottom chrome extends so wide that its outer corners are visibly clipped by the iPhone's rounded display corners. On one item, the bottom chrome is noticeably less wide and sits differently. The media title and private library details are intentionally omitted.

    Revised code finding / leading hypothesis

    The normal corner clipping is consistent with the current presentation chain applying .ignoresSafeArea() to the entire CustomPlayerView, including its chrome, then giving phone-landscape bottom controls only an 8 pt outer horizontal inset. In other words, the platter is allowed to approach the full physical display bounds rather than an explicit rounded-corner-safe content guide.

    The platter width is also not explicitly anchored at the call site with a stable frame(maxWidth: .infinity) or a named safe-area/container width. It relies on proposal propagation through the bottom VStack into controls, then through phoneLandscapeControls; the latter contains a flexible full-width scrubber plus a content-sized/fixed menu row. A different chrome branch, available-menu set, or transient viewport proposal can therefore plausibly produce a narrower resolved envelope even though video aspect is not a direct input.

    The highest-value checks are now:

    1. Log/overlay the full-screen bounds, safe-area insets, chromeViewportSize, selected isPhoneLandscapeChrome branch, bottom controls frame, and platter frame for a normal clipped item versus the narrower item.
    2. Record availableMenus and PiP availability so a content/capability-driven intrinsic-width change is visible.
    3. Capture the first layout pass and the settled pass after landscape/full-screen presentation; confirm an initial zero/portrait/narrow proposal cannot leave the platter on a different width.
    4. Compare AVPlayerLayer.videoRect separately, but do not use it as the leading explanation for the chrome-width difference.

    The leading hypothesis is now an under-constrained bottom-chrome width combined with whole-player safe-area ignoring and/or a transient layout-policy proposal, not active-video-rectangle sizing. The intended fix direction, once measured, should be a single stable screen/safe-area-relative chrome envelope for equivalent iPhone landscape states, with deliberate rounded-corner clearance rather than accidental clipping.

  4. added
    bugSomething isn't working
    playerIn-player UI / playback
    priority:p1High priority; release/publication blocker or major user impact
    status:needs-reproNeeds reliable reproduction or evidence before implementation
    on Aug 22, 2026
  5. jlipworth commented on Sep 29, 2026

    @jlipworth
    OwnerAuthor

    Release-owner decision: defer this issue for the 1.7.2 (build 1) candidate. Keep it open; this is an explicit scope deferral, not a fix or passing acceptance result. Candidate preparation and remaining release gates are recorded in #332.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingplatform:iosiPhone and iOSplatform:ipadosiPad and iPadOSplayerIn-player UI / playbackpriority:p1High priority; release/publication blocker or major user impactstatus:needs-reproNeeds reliable reproduction or evidence before implementationui

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions