Search Unity

Unity SafeArea is inconsistent between different starting rotations

Discussion in 'iOS and tvOS' started by Nerrolken, Sep 23, 2021.

  1. Nerrolken

    Nerrolken

    Joined:
    Jun 10, 2017
    Posts:
    7
    I'm trying to make a script that adjusts the SafeArea of my Canvas when the device is rotated. I've got it almost working and it looks great on-launch, but I'm getting a weird result after rotating that I can't understand: the SafeArea is different depending on whether the app launched in Portrait or Landscape mode.

    When I log out the value of Screen.safeArea on an iPhone 12 Pro, for example, I get the following values:

    - Portrait on launch: (x:0.00, y:102.00, width:1170.00, height:2289.00)
    - Portrait rotated to Landscape: (x:0.00, y:102.00, width:2532.00, height:927.00)
    - Landscape on launch: (x:141.00, y:63.00, width:2250.00, height:1107.00)
    - Landscape rotated to Portrait: (x:141.00, y:63.00, width:888.00, height:2469.00)

    The Width/Height values are changing on rotation, which makes sense, but the X/Y offsets are not. Given the particular nature of the iPhone 12, that means there is a big empty space at the top when launching in Portrait and rotating to Landscape but overflowing the notch to the side, and big empty spaces on both sides when launching in Landscape and rotating to Portrait but overflowing the notch to the top.

    Is this the expected behavior? I would have thought the "launching in Portrait" and "rotated to Portrait" safeAreas should be the same, right? Is there a way to force-refresh the safeArea or something, to get the proper X/Y values? Or am I misunderstanding how this is supposed to work?
     
  2. Alexey

    Alexey

    Unity Technologies

    Joined:
    May 10, 2010
    Posts:
    1,624
    This is indeed a bug that we are aware of, but alas we dont know how to fix this (yet, hopefully ;-))
     
  3. Nerrolken

    Nerrolken

    Joined:
    Jun 10, 2017
    Posts:
    7
    Thanks for the reply! Are there any recommended workarounds for now, or best practices, etc?
     
  4. jj1991

    jj1991

    Joined:
    Jul 19, 2017
    Posts:
    18
    Hi, I have a similar problem, the safe screen is not correct anymore, I even downgraded unity to a version which used to work and it has the same problem, so I don't know how I can update my app anymore since I cannot find the cause. It seems like an issue with iOS15
     
    Last edited: Oct 7, 2021
  5. dsFury

    dsFury

    Joined:
    Oct 17, 2019
    Posts:
    19
    Any news on this? @Alexey
     
  6. fury_ivan

    fury_ivan

    Joined:
    Oct 7, 2020
    Posts:
    2
    Noticed that this problem started happening after I've updated Xcode to version 13. I would recommend downloading Xcode 12.5.1 or some older one and then making a build. As I got the problem is in Xcode, not in iOS15 or Unity.

    You can find older Xcode versions here:
    https://developer.apple.com/download/all/?q=xcode 12.5.1

    I hope this will help! Cheers!
     
  7. Markusn

    Markusn

    Joined:
    Jul 15, 2012
    Posts:
    22
    Ran right into this yesterday. Any updates?
     
  8. jj1991

    jj1991

    Joined:
    Jul 19, 2017
    Posts:
    18
    I tried Xcode 12.5.1, I have the same problem, Screen.Safearea returns wrong values for iPhone 12 Pro in Landscape mode (all 4 margins are wrong), basically left and right margins are missing completely, the top and bottom are there but wrong. The Landscape values are correct if I start the application in Landscape, but then the Portrait values are wrong when I flip the phone and vice-versa. I temporarily solved the first problem by getting the safeArea with a native script, I limited this case to only iPhone and Landscape.
     
    Last edited: Oct 15, 2021
  9. abcjjy

    abcjjy

    Joined:
    Mar 6, 2015
    Posts:
    35
    I ran into this issue today. It is related to iOS15, not Xcode. Safe area is correct on iOS14.8 compiled with Xcode13.1 and Unity2018.4.36.

    @jj1991 Can you share your native script?
     
  10. jj1991

    jj1991

    Joined:
    Jul 19, 2017
    Posts:
    18
    Yeah sure here, not sure if it works correctly on every device but better than the bug and the problem with starting the app in landscape and then turning to portrait is not solved
    Code (CSharp):
    1. public class iOSSafeArea : MonoBehaviour
    2. {
    3.     [DllImport("__Internal")]
    4.     public static extern string getSafeArea();
    5.  
    6.     public static Rect _getSafeArea()
    7.     {
    8.         float scaleFactor = DeviceDisplay.scaleFactor;
    9.         string[] margins = getSafeArea().Split(':');
    10.         float top = float.Parse(margins[0])* scaleFactor;
    11.         float bottom = float.Parse(margins[1]) * scaleFactor;
    12.         float right = float.Parse(margins[2]) * scaleFactor;
    13.         float left = float.Parse(margins[3]) * scaleFactor;
    14.  
    15.         Rect rect = new Rect(); //new Rect(right, top,(Screen.width- left), (Screen.height-bottom));
    16.  
    17.    
    18.         rect.xMin = left;
    19.         rect.yMin = bottom;
    20.         rect.xMax = Screen.width - right;
    21.         rect.yMax = Screen.height;
    22.  
    23.         return rect;
    24.  
    25.     }
    Code (CSharp):
    1.  
    2. #import <AVFoundation/AVFoundation.h>
    3.  
    4. extern "C" {
    5.  
    6. char* convertNSStringToCString(const NSString* nsString)
    7. {
    8.     if (nsString == NULL)
    9.         return NULL;
    10.  
    11.     const char* nsStringUtf8 = [nsString UTF8String];
    12.     //create a null terminated C string on the heap so that our string's memory isn't wiped out right after method's return
    13.     char* cString = (char*)malloc(strlen(nsStringUtf8) + 1);
    14.     strcpy(cString, nsStringUtf8);
    15.  
    16.     return cString;
    17. }
    18.  
    19. char* getSafeArea() {
    20.     UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
    21.        CGFloat topPadding = window.safeAreaInsets.top;
    22.        CGFloat bottomPadding = window.safeAreaInsets.bottom;
    23.        CGFloat rightPadding = window.safeAreaInsets.right;
    24.        CGFloat leftPadding = window.safeAreaInsets.left;
    25.  
    26.     NSString *str = [NSString stringWithFormat:@"%f:%f:%f:%f", topPadding, bottomPadding,rightPadding,leftPadding];
    27.  
    28.     return convertNSStringToCString(str);
    29. }
    30.  
    31. }
     
  11. CodyJChartrand

    CodyJChartrand

    Joined:
    Sep 6, 2018
    Posts:
    4
    Any news on this being fixed any time soon? Seeing this issue on the iPhone 13 running iOS 15.
     
  12. jj1991

    jj1991

    Joined:
    Jul 19, 2017
    Posts:
    18
  13. NUTRACTOR

    NUTRACTOR

    Joined:
    Dec 1, 2014
    Posts:
    8
    Search for the file UnityView.mm from the iOS build project.
    Then modify the function ComputeSafeArea(UIView* view).

    Code (CSharp):
    1. CGRect ComputeSafeArea(UIView* view)
    2. {
    3.     UIWindow *window = UnityGetMainWindow();
    4.     CGRect screenRect = window.bounds;
    5.     float scale = window.screen.scale;
    6.    
    7.     UIEdgeInsets insets = window.safeAreaInsets;
    8.    
    9. //    CGSize screenSize = view.bounds.size;
    10. //    CGRect screenRect = CGRectMake(0, 0, screenSize.width, screenSize.height);
    11. //
    12. //    UIEdgeInsets insets = [view safeAreaInsets];
    13.    
    14.     screenRect.origin.x += insets.left;
    15.     screenRect.origin.y += insets.bottom; // Unity uses bottom left as the origin
    16.     screenRect.size.width -= insets.left + insets.right;
    17.     screenRect.size.height -= insets.top + insets.bottom;
    18.    
    19. //    float scale = view.contentScaleFactor;
    20.    
    21.     // Truncate safe area size because in some cases (for example when Display zoom is turned on)
    22.     // it might become larger than Screen.width/height which are returned as ints.
    23.     screenRect.origin.x = (unsigned)(screenRect.origin.x * scale);
    24.     screenRect.origin.y = (unsigned)(screenRect.origin.y * scale);
    25.     screenRect.size.width = (unsigned)(screenRect.size.width * scale);
    26.     screenRect.size.height = (unsigned)(screenRect.size.height * scale);
    27.     return screenRect;
    28. }
    29.  
     
    Anders_Pe likes this.
  14. NUTRACTOR

    NUTRACTOR

    Joined:
    Dec 1, 2014
    Posts:
    8
    Note that safeAreaInsets of UIWindow does not return the correct value in some models without a home bar.
     
  15. NUTRACTOR

    NUTRACTOR

    Joined:
    Dec 1, 2014
    Posts:
    8
    Since the size of UIWindow is different from that of Unity, it is better to get only safeAreaInsets in the plugin and create safeArea in Unity side.
    The approach of jj1991 is better.
     
  16. NUTRACTOR

    NUTRACTOR

    Joined:
    Dec 1, 2014
    Posts:
    8
  17. MartinG

    MartinG

    Joined:
    Apr 17, 2013
    Posts:
    16
    we also run into the same problem. It's only appearing on iOS 15 devices. Nothing to do with different Xcode versions or iPhone models. Same iPhone with iOS 14.4 works fine, iPhone with 15.1 delivers wrong safeArea values after rotation from portrait to landscape.
     
  18. MartinG

    MartinG

    Joined:
    Apr 17, 2013
    Posts:
    16
    Digging deeper we found out, that the problem seems to be related to both, XCode 13 and iOS 15. An XCode 13 build runs on iOS 14 device with correct safeArea after rotation. Same build on iOS13 device has the safeArea Bug after rotation. A build with XCode 12.5.1 run on iOS14 and iOS15 devices as expected.

    We have apps built with Unity 2018.4.34. A fix for Unity 2018 LTS would be awesome!
     
    tessellation likes this.
  19. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    I have a solution that fixes all these issues. We couldn't wait for Unity to backport the fix to Unity 2020 LTS. According to their own bug report, Unity fixed the issues in Unity 2022.1.0.a14. So I installed the latest 2022.1.0b2 and compared the Obj-C source code in PlaybackEngines/iOSSupport/Trampoline/Classes folders between 2022.1.0 and 2020.3.25f1 LTS that we're using. If you do the same, you can see the fixes they made.

    Copy the following files to Unity 2020 and then it works:

    UI/SplashScreen.mm
    UI/UnityAppController+ViewHandling.mm
    UI/UnityView.mm
    UI/UnityViewControllerBase.*
    UI/UnityViewControllerBase+iOS.*
    Unity/DeviceSettings.mm
    Unity/DisplayManager.mm
     
    Last edited: Jan 6, 2022
    rbcode and Alan-Liu like this.
  20. abcjjy

    abcjjy

    Joined:
    Mar 6, 2015
    Posts:
    35
    After experimenting for hours, I found the cause of this issue. In UI/UnityAppController+ViewHandling.mm
    - (void)showGameUI,

    Code (objc):
    1.  
    2. 169     // UI hierarchy
    3. 170     [_window addSubview: _rootView];
    4. 171     _window.rootViewController = _rootController;                                                    
    5. 172     [_window bringSubviewToFront: _rootView];
    6.  
    This code is quite unusual. Content view will be set in rootViewController setter and it is a common use. Why to addSubView manually? On iOS 15, it makes the UnityView's safe area can't update with UIWindow.

    Comment out the addSubview line and the safeAreaInsets/LayoutGuide will be corrent.

    You can edit that line in /Applications/Unity/Hub/Editor/2019.4.34f1/PlaybackEngines/iOSSupport/Trampoline/Classes/UI/UnityAppController+ViewHandling.mm to fix this issue for all future builds.

    And this issue also causes a bug in Admob banner placement which depends on the safe area properties of the UnityView.
     
    Last edited: Jan 13, 2022
  21. rbcode

    rbcode

    Joined:
    Aug 18, 2020
    Posts:
    4
    tesselation´s workaround worked for us. Thanks for sharing.
    I attached the relevant files in a zip-archive. Files are from version 2022.1.0b8
     

    Attached Files:

  22. Fun2Momo

    Fun2Momo

    Joined:
    Jul 23, 2021
    Posts:
    1
    I follow tesselation´s workaround, copy those files from 2022.1.0b8 to 2020.3.22f1.

    But build Xcode result is failed with error message :
    Code (CSharp):
    1. No type or protocol named 'UnityViewControllerNotifications'
    Am I missing anything?

     
    Last edited: Feb 23, 2022
  23. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Yes, this seems to be the root cause, thanks for investigating. In Unity 2022.2, the Unity team has moved this line to the - (void)createUI method with the following comment added:

    Code (objc):
    1. // We need to add the root view to the view hierarchy before initializing graphics,
    2. // as plugins might need to access view properties (e.g. safeAreaInsets). Otherwise,
    3. // they will get default values if the view is not yet added to the window.
    4. [_window addSubview: _rootView];
    So it might be wise to do what they do and move the code instead of removing it altogether. I don't know much about Apple's UIKit, so I value your experience.
     
  24. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    I'm not sure why you're getting this error. This part of the code shouldn't have changed. Try doing a clean build in XCode and make sure you're only copying the files I mentioned and no others.
     
  25. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Update: I just noticed that with the latest release of Unity 2020.3.31, there have been some new changes to these source files. So to avoid breaking things, please diff and merge the changes to Unity 2020 instead of copying and overwriting the files directly. Again, it's likely that UnityAppController+ViewHandling.mm contains the relevant code to fix this bug and so you may not need to copy the other changes mentioned.
     
  26. abcjjy

    abcjjy

    Joined:
    Mar 6, 2015
    Posts:
    35
    Unity 2019.4.37 is released, which contains a backported fix.
     
  27. DanRP

    DanRP

    Joined:
    May 26, 2013
    Posts:
    33
    FYI: I'm experiencing this same issue on Unity 2022.3.7f1
     
  28. jj1991

    jj1991

    Joined:
    Jul 19, 2017
    Posts:
    18
  29. unity_03C343CF822C8A61A49A

    unity_03C343CF822C8A61A49A

    Joined:
    Oct 17, 2023
    Posts:
    1
    I met the situation that safe area works fine on editor, but when built to device (mine is Iphone 11) safe area is not working at all :(
     
  30. peacefulshade

    peacefulshade

    Joined:
    Jul 25, 2012
    Posts:
    12
    @Alexey idk if u guys are aware that the issue came back in 2022 LTS, thnx
     
    Atair_ likes this.