margelo / margelo/react-native-vision-camera

🐛 iOS orientationSource="interface" does not update on programmatic UI orientation locks (Android does)

Open
#4,109 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

🐛 bug
Dominant language
TypeScript
Stars
9.6k
Forks
1.4k
Avg merge
1d 28m
Merged PRs (30d)
4

Description

Prerequisites
Reproduction

https://github.com/ivansinuyk/vision-camera-ios-interface-orientation-repro

Steps to reproduce

Reproduction (simplified)

  1. App starts in portrait.
  2. Navigate to a camera screen.
  3. Immediately lock UI to landscape (OrientationDirector.lockTo(landscapeRight) or equivalent).
  4. Mount without waiting for the rotation animation.
  5. Android: preview/output becomes landscape.
  6. iOS: preview/output often stays portrait (rotated 90°) even though the UI is landscape.
    If Camera is mounted only after the lock animation finishes, iOS looks fine because init reads the already-updated interfaceOrientation once — but that is an app workaround, not correct continuous listening.
What did you expect to happen?

With orientationSource="interface":

  • When the app UI rotates to landscape (including programmatic locks), VisionCamera should update outputOrientation / preview to match the new interface orientation.

  • Behavior should match the docs: interface mode follows UI orientation / screen lock, not only physical tilt.

What actually happened?

Android | Works. Display rotation changes → camera orientation updates.

iOS | Often stuck. Programmatic landscape lock updates UI, but VisionCamera does not update until something else triggers a refresh (or Camera is remounted after rotation completes).

Affected platforms

iOS (device)

Device(s) affected

iPhone 17 Pro Max

VisionCamera version

5.1.1

React Native version

0.86.0

React Native architecture

New Architecture (Fabric / bridgeless)

Features being used
  • Preview
  • Photo capture
  • Video capture
  • Frame Processors (worklets)
  • Skia Frame Processors
  • Code/Barcode Scanner
  • Location metadata
  • Multi-cam
  • Depth data
  • HDR / custom dynamic range
  • Custom format / FPS / resolution
Relevant logs / stack trace
No crash — behavioral bug. Logs from physical iPhone while reproducing
programmatic landscape lock + VisionCamera.createOrientationManager('interface')
Additional context

On Android, orientationSource="interface" correctly follows app UI orientation changes driven by a programmatic lock (e.g. locking the screen to landscape when opening a camera screen).

On iOS, the same setup often keeps the camera preview/output stuck in the previous orientation (commonly portrait) when the app locks to landscape via an orientation locker library. Physical device rotation still works; programmatic interface locks do not.

This forces apps to delay mounting on iOS until after the UI rotation settles — Android does not need that workaround.

Android – HybridInterfaceOrientationManager listens to DisplayManager.DisplayListener.onDisplayChanged and reads display rotation. Activity orientation locks update the display, so the camera follows.

iOS – HybridInterfaceOrientationManager listens to UIDevice.orientationDidChangeNotification (physical device tilt), then reads UIApplication.shared.interfaceOrientation.

That notification does not reliably fire for programmatic interface locks (requestGeometryUpdate / orientation director). So UI becomes landscape while VisionCamera never receives an update and keeps the old orientation.

Relevant iOS code (v5.1.1):

// HybridInterfaceOrientationManager.swift
self.observer = NotificationCenter.default.addObserver(
  forName: UIDevice.orientationDidChangeNotification, // physical device
  object: nil,
  queue: .main
) { _ in
  let interfaceOrientation = UIApplication.shared.interfaceOrientation // UI
  // ...
}

So iOS “interface” mode is driven by a device signal, while Android “interface” mode is driven by a display/UI signal.

What we need
Please make iOS OrientationSource.interface track UI / interface orientation changes, including programmatic locks, similar to Android’s display listener.

Suggested approach (any of these):

Stop relying solely on UIDevice.orientationDidChangeNotification for interface mode.
Also observe real interface-orientation transitions (and/or poll windowScene.interfaceOrientation while updates are active).
Emit/sync current UIApplication.shared.interfaceOrientation when startOrientationUpdates begins, and again whenever it changes due to geometry updates / locks.
Workaround we verified
A local patch that:

keeps the existing device-orientation observer
also observes UIApplication.didChangeStatusBarOrientationNotification
polls UIApplication.shared.interfaceOrientation at a low rate while updates are active
…fixes iOS and lets us remove the “delay Camera mount until lock settles” workaround.

Happy to open a PR if useful.

You can attach your patch file

react-native-vision-camera-npm-5.1.1-93b71ffaa4.patch

) or a short screen recording of iOS vs Android if you want to make it stronger.

Submission
  • The reproduction I linked is either (preferred) a PR against this repo that adds a failing harness test following the harness-tests README, or (fallback) a public repo that reproduces the bug on a fresh clone. I understand the issue will be closed without one.
  • I pasted logs as text (not screenshots).
  • I wrote this report in my own words. I did not paste AI-generated descriptions of the bug.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with HybridInterfaceOrientationManager.swift and the startOrientationUpdates entry point, then reproduce the programmatic landscape lock using the linked reproduction. Compare the existing UIDevice orientation notification handling with interface-orientation changes, and consider the harness-tests README for validation; done means orientationSource="interface" updates the iOS preview and output when the UI is locked programmatically, without delaying Camera mounting.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, react-native, swift, typescript
Domain
mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.