Skip to content

Fix iOS orientation updates during transitions#4117

Draft
sorinc03 wants to merge 2 commits into
software-mansion:mainfrom
sorinc03:fix/ios-enforce-orientation-during-transition
Draft

Fix iOS orientation updates during transitions#4117
sorinc03 wants to merge 2 commits into
software-mansion:mainfrom
sorinc03:fix/ios-enforce-orientation-during-transition

Conversation

@sorinc03

@sorinc03 sorinc03 commented May 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • On iOS 16+, request orientation geometry updates from the desired supported-orientation mask directly instead of first relying on windowScene.interfaceOrientation.
  • Refresh the top view controller's supported-orientation state before requesting geometry updates.
  • Retry a failed geometry update once on the next main-queue turn and warn if UIKit still rejects it, instead of silently swallowing the failure.

Root cause

enforceDesiredDeviceOrientation gated iOS 16+ requestGeometryUpdate behind a pre-iOS-16 heuristic that compares the current device and scene interface orientations. During navigation transitions, scene restoration, or after previous geometry updates, windowScene.interfaceOrientation can be stale. When the stale orientation still appears to satisfy the new mask, the method skips the iOS 16+ geometry request completely.

UIKit may also reject a transition-time request while its supported-orientation cache still reflects the outgoing screen. The previous error handler was empty, so these failed requests disappeared without another attempt.

Reproduction

This is easiest to reproduce with the Expo repro from the issue because it exercises native-stack per-screen orientation changes during a real iOS transition.

  1. git clone https://github.com/JeanDes-Code/orientation-lock-repro
  2. cd orientation-lock-repro
  3. npm install
  4. Delete patches/react-native-screens+4.23.0.patch so the app runs against unpatched react-native-screens. The expo-screen-orientation patch can remain.
  5. npm install
  6. npx expo prebuild --clean
  7. npx expo run:ios
  8. Tap Go to Landscape (Option C). This navigates to a native-stack screen with options={{ orientation: 'landscape_right' }}.
  9. Tap Go Back. The app should return to portrait, but without this fix it can stay stuck in landscape.
  10. Repeat the landscape/back cycle once or twice. The orientation lock can then fail in both directions.

The app-level repro is equivalent to this single-file native-stack setup:

import React from 'react';
import { Button, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

function PortraitScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button
        title="Go to Landscape"
        onPress={() => navigation.push('Landscape')}
      />
    </View>
  );
}

function LandscapeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="Go Back" onPress={() => navigation.goBack()} />
    </View>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{ orientation: 'portrait' }}>
        <Stack.Screen name="Portrait" component={PortraitScreen} />
        <Stack.Screen
          name="Landscape"
          component={LandscapeScreen}
          options={{ orientation: 'landscape_right' }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Validation

  • yarn check-types
  • yarn lint-swift
  • xcodebuild -workspace FabricExample/ios/FabricExample.xcworkspace -scheme FabricExample -configuration Debug -destination "platform=iOS Simulator,name=iPhone 17 Pro Max,OS=latest" -derivedDataPath /Users/sorincioban/Library/Developer/Xcode/DerivedData/RNScreensOSS CODE_SIGNING_ALLOWED=NO build

Fixes #3831

@kmichalikk kmichalikk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. Please answer my comments. Also, I would like to have a step-by-step instructions on how to reproduce the bug. Most preferably, prepare an issue test in our repo with a code that could be put into one file. If expo is required, then paste the file in the PR and tell what to do besides create-expo-app, but please don't add unnecessary dependencies, expo-router, etc, if not necessary.

Comment thread ios/RNSScreenWindowTraits.mm Outdated
Comment thread ios/RNSScreenWindowTraits.mm Outdated
@kmichalikk kmichalikk requested a review from kkafar June 2, 2026 07:28
@sorinc03

sorinc03 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Updated the PR description with step-by-step reproduction instructions and a one-file native-stack example. The branch already includes the iOS 16 availability guard and resolves the orientation update controller from the scene used for the geometry request.

@kmichalikk kmichalikk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested some more and I can see that on our FabricExample the one-file example works correctly without patches, on expo with react-navigation template it works only if expo-dev-client is uninstalled, and it fails if it is installed, patch doesn't help there. So I am not able to successfully reproduce & see it being fixed, I don't know how to proceed here.

cc @kkafar

@sorinc03

sorinc03 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for retesting. I agree this is a blocker for the PR as currently framed.

I checked the external repro again, and it is not a pure react-native-screens case: its README/workaround combines two changes:

  • an expo-screen-orientation fix for finding the RNScreens controller behind DevLauncherViewController
  • a react-native-screens change around the iOS 16+ geometry update path

This PR only targets the second part, so if the expo-dev-client setup still fails with this branch, I do not want to claim that this fixes that full repro. I will try to isolate a smaller case that fails on main and passes here, preferably in FabricExample / without expo-dev-client. If I cannot produce that, I agree the PR should not be merged as-is and I will close or reframe it.

@sorinc03 sorinc03 marked this pull request as draft June 8, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enforceDesiredDeviceOrientation silently fails on iOS 16+ during screen transitions

2 participants