margelo / margelo/react-native-vision-camera
🐛 iOS orientationSource="interface" does not update on programmatic UI orientation locks (Android does)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 9.6k
- Forks
- 1.4k
- Avg merge
- 1d 28m
- Merged PRs (30d)
- 4
Description
Prerequisites
- I have read and followed every applicable step in the Nitro Modules Troubleshooting guide.
- I have read the VisionCamera Troubleshooting guide.
- I have searched existing issues and found nothing matching.
- I am on the latest version of react-native-vision-camera, or have a specific reason I cannot upgrade.
Reproduction
https://github.com/ivansinuyk/vision-camera-ios-interface-orientation-repro
Steps to reproduce
Reproduction (simplified)
- App starts in portrait.
- Navigate to a camera screen.
- Immediately lock UI to landscape (OrientationDirector.lockTo(landscapeRight) or equivalent).
- Mount without waiting for the rotation animation.
- Android: preview/output becomes landscape.
- 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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