Prerequisites
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
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
Prerequisites
Reproduction
https://github.com/ivansinuyk/vision-camera-ios-interface-orientation-repro
Steps to reproduce
Reproduction (simplified)
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
Relevant logs / stack trace
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):
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