Skip to content

Incorrect layout when navigating between screens of different orientation on iOS #1738

@thomas-coldwell

Description

@thomas-coldwell

Description

This issue is hard to consistently reproduce, however, when navigating between screens with a different orientation e.g. Landscape -> Portrait, the new screen is incorrectly laid out with the previous screen's orientation - see screenshot below of the view hierarchy after pushing a new portrait screen.

Screenshot 2023-03-14 at 16 06 14

Simulator.Screen.Recording.-.iPhone.14.-.2023-03-14.at.18.29.56.mp4

As you can see the RNSScreenView (child of the RNSScreen UIViewController) that I've navigated to has had its frame set to landscape dimensions even though it is actually a portrait screen, resulting in a broken layout.

I've added a log to the setFrame method of the RNSScreenView to see where this gets called from and with what dimensions (if you scroll to the right you can see the calling method):

// When it breaks ❌:
2023-03-12 20:41:11.536827+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 0.000000x0.000000 called from 2   UIKitCore                           0x0000000131f58325 UIViewCommonInitWithFrame + 1849
2023-03-12 20:41:11.796876+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x390.000000 called from 2   UIKitCore                           0x0000000130f725fa -[UINavigationController _startCustomTransition:] + 1348
2023-03-12 20:41:11.809961+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x390.000000 called from 2   OrientationTest                            0x0000000104cdeff8 -[RNSScreenStackAnimator animateTransition:] + 392
2023-03-12 20:41:11.823229+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x390.000000 called from 2   OrientationTest                            0x0000000104ce0aa2 -[RNSScreenStackAnimator animateFadeWithTransitionContext:toVC:fromVC:] + 338

// When it lays out correctly ✅:
2023-03-12 20:43:29.145651+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 0.000000x0.000000 called from 2   UIKitCore                           0x0000000131f58325 UIViewCommonInitWithFrame + 1849
2023-03-12 20:43:29.401373+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x390.000000 called from 2   UIKitCore                           0x0000000130f725fa -[UINavigationController _startCustomTransition:] + 1348
2023-03-12 20:43:29.408939+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x390.000000 called from 2   OrientationTest                            0x0000000104cdeff8 -[RNSScreenStackAnimator animateTransition:] + 392
2023-03-12 20:43:29.417094+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x390.000000 called from 2   OrientationTest                            0x0000000104ce0aa2 -[RNSScreenStackAnimator animateFadeWithTransitionContext:toVC:fromVC:] + 338
2023-03-12 20:43:29.519909+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 844.000000x844.000000 called from 2   UIKitCore                           0x0000000130f89f46 -[UINavigationController _layoutViewController:] + 1194
2023-03-12 20:43:29.820028+0000 OrientationTest[49171:3980390] 👉RNSScreenView setting frame to 390.000000x844.000000 called from 2   UIKitCore                           0x0000000130f89f46 -[UINavigationController _layoutViewController:] + 1194

There are a couple of interesting things to note in the logs above:

  • Firstly, the [UINavigationController _startCustomTransition:] & related transition animation methods set the new screen to landscape dimensions (even though the screen is specified as portrait). It looks like this is due to the fact the UINavigationController's supportedInterfaceOrientations are specified by its top view controller which in this case would still be landscape (before the transition has started and before the new view controllers have been set) & then the UINavigationController internally sets the final frame for the view we are transitioning to based on this in its various animated transition methods.
  • Secondly, in the case where the layout is correct, there is a final [UINavigationController _layoutViewController:] presumably from it some other update to the navigation controller triggering a re-layout with the correct, portrait dimensions for the screen.

Expected Behaviour

That when navigating to a portrait screen it should correctly layout with portrait dimensions.

Steps to reproduce

  1. Open example on an iPhone simulator (we've been able to reproduce it on a physical device, but its most easily reproducible here for some reason)
  2. Navigate back and forth between Landscape -> Portrait screens
  3. Observe the calls to setFrame in the RNSScreenView
  4. Eventually the layout should break as shown in the above screenshot (this might take a number of tries)
  5. See that the Portrait screen is laid out with Landscape dimensions in the XCode view hierarchy debugger

Snack or a link to a repository

https://github.com/thomas-coldwell/rnscreen-orientation-glitch

Screens version

3.20.0

React Native version

0.71.3

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

iOS simulator

Device model

iPhone 14

Acknowledgements

Yes

Metadata

Metadata

Assignees

Labels

Platform: iOSThis issue is specific to iOSRepro providedA reproduction with a snack or repo is provided

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions