Hi, I’m currently working on an app which was originally designed to be portrait mode only, however we added a new scene which should handle both portrait and landscape modes, so I added a script which enables autorotation when entering the screen, and disable rotation and set the rotation back to portrait when OnDestroy is invoked.
Parameters set from the editor: enableAutorotate = true, initialOrientation = ScreenOrientation.AutoRotation, finalOrientation = ScreenOrientation.Portrait
public class ScreenOrientationController : MonoBehaviour
{
[Header("Autorotation")]
[Tooltip("Enable autorotation for the lifetime of this object.")]
public bool enableAutorotate;
[Header("Before Start")]
[Tooltip("Initial orientation when this object spawns.")]
public ScreenOrientation initialOrientation = ScreenOrientation.Portrait;
[Header("On Destroy")]
[Tooltip("Final Orientation will be enforced whenever this object is destroyed. If the 'Enable Autorotate' was enabled, autorotation will be disabled on destroy.")]
public ScreenOrientation finalOrientation = ScreenOrientation.Portrait;
// Start is called before the first frame update
void Start()
{
Screen.orientation = initialOrientation;
if (enableAutorotate)
{
Screen.autorotateToPortrait = true;
Screen.autorotateToLandscapeLeft = true;
Screen.autorotateToLandscapeRight = true;
// do not support upside down orientation.
Screen.autorotateToPortraitUpsideDown = false;
}
}
void OnDestroy()
{
if (enableAutorotate)
{
Screen.autorotateToPortrait = false;
Screen.autorotateToLandscapeLeft = false;
Screen.autorotateToLandscapeRight = false;
// do not support upside down orientation.
Screen.autorotateToPortraitUpsideDown = false;
}
Screen.orientation = finalOrientation;
}
}
>
On Android, it works regardless of the state before leaving the scene.
On iOS, there’s 2 scenarios: one which works, and one which doesn’t:
if you are in landscape and switch back to the other scene, OnDestroy is invoked - the device is set to portrait and autorotation is disabled.
If you’re in portrait mode and switch back, OnDestroy is invoked - the device remains in portrait mode but autorotation is not disabled.
Is this a bug or I’m missing some sort of specific handling for iOS devices?
It smells like a bug… but I would first try read the device logs just to make sure there isn’t some error being thrown that you might be able to handle.
After lots of testing and debugging, it seems that if the orientation isn’t updated, the autorotation flags are not updated either. In other words, if you update from portrait to portrait, the value is still ‘portrait’, it doesn’t trigger the update.
fetching system logs from the device simulator I can see that the Screen Orientation preferences are updated when opening the new scene, and updated if you leave the scene while in landscape mode. However, exiting the scene while in portrait doesn’t trigger the Screen Orientation preferences update.
Logs 1: Switching to landscape before leaving scene 2
<Entering Scene 2>
default 17:10:45.703697+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene updated orientation preferences: ( Pu ) -> ( Pu Ll Lr )
<Rotating screen>
default 17:11:51.322990+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene will change interface orientation: landscapeLeft (4)
<Leaving Scene 2>
default 17:12:22.067449+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene updated orientation preferences: ( Pu Ll Lr ) -> ( Ll )
default 17:12:22.068434+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene updated orientation preferences: ( Ll ) -> ( Pu Ll Lr )
default 17:12:22.071389+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene updated orientation preferences: ( Pu Ll Lr ) -> ( Ll )
default 17:12:22.072987+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene updated orientation preferences: ( Ll ) -> ( Pu )
default 17:12:22.117352+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene will change interface orientation: portrait (1)
Logs 2: Leaving without changing screen orientation (always portrait)
<Entering Scene 2>
default 17:16:54.155374+0900 RotationBug <UIWindowScene: 0x7fbc4950a800> (C63991C1-1106-4332-918D-1D24676D61B8) Scene updated orientation preferences: ( Pu ) -> ( Pu Ll Lr )
<Leaving Scene 2>
The Orientation prefs are not updated, leaving the app in a bugged state. Should I file in a bug report? Is the problem that I’m attempting to update the orientation preferences from the OnDestroy method, instead of invoking something when the <leave scene 2> button is pressed ?
BTW, the above logs are system logs for the device, not from my app. I did add debug messages which only showed that the code was being executed as intended.
What happens if you execute the “disable autorotation” code a few frames in a row, or even just call it delayed by a frame or two? It’s hacky but there’s other precedent in the Unity engine, such as loading a scene and setting it active (you must wait a frame).
I even keep a handy “Do something later” class. You can even mark these DontDestroyOnLoad so they survive one scene to the next for something like this.
You could always try changing that but I kinda doubt that’s the issue. Personally I almost never do things in OnDestroy() simply because it complicates what happens in the editor, but rather I always use OnDisable. But OnDestroy() is still a legitimate callback…
Back in the days of Google VR, I wrote a coroutine to force the Screen.orientation to a certain value.
Because often, setting it once wasn’t doing anything on iOS. It just failed. Setting it in an update loop was overkill.
So I made a coroutine. Setting the requested value, waiting a few frames and checking the value whether it is the set value or not. If not, repeat by setting it again. Trying again until the retry count has reached 0 or the value is set.
It would’ve been so much easier if there were callbacks from Unity whether a screen orientation change was successful or not. Or even a callback when a screen orientation change happens would’ve been nice.
If forcing it doesn’t work, you might want to go for a bug report.
I tried the solution with the class you provided, late invoke didn’t do anything to fix it. What’s more, QA recently reported a new bug and… seems iOS 16 changed the way the system reacts to updating orientation preferences. Guess I’ll have to implement my own orientation controller to keep everything under MY control, instead of letting Unity/iOS handle it their own way.
This seems like the best way to do it, despite being a workaround. The OS/game engine should be able to handle this out of the box. (Actually it works fine on Android, so… maybe it’s just an iOS thing?)
Hence I find it weird that they haven’t made some screen orientation callback API yet.
Something so basic, yet still missing. Despite having put in a request for this, it never got picked up.
Requesting permissions is (or was) an annoyance too. You’d wish there was some API for this to handle requesting certain permissions. You mostly had to create your own native plugin to take care of the functionality needed.
(Just looked at the documentation) They did it for Android with the Permissions class, but not for iOS it seems.
Most people would point out some asset on the asset store rather than requesting this functionality from Unity.
But imo things like this should be made easy by the engine. Not some asset from the asset store.