暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

UnityViewControllerBase+iOS.mm 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #if PLATFORM_IOS
  2. #import "UnityViewControllerBase.h"
  3. #import "UnityAppController.h"
  4. #include "OrientationSupport.h"
  5. #include "Keyboard.h"
  6. #include "UnityView.h"
  7. #include "PluginBase/UnityViewControllerListener.h"
  8. #include "UnityAppController.h"
  9. #include "UnityAppController+ViewHandling.h"
  10. #include "Unity/ObjCRuntime.h"
  11. // when orientation changes with animation, pause rendering for this long so resolution change happens during animation with less visual distortion
  12. const NSTimeInterval REORIENTATION_RENDERING_PAUSE = 0.15;
  13. // when returning from presenting UIViewController we might need to update app orientation to "correct" one, as we wont get rotation notification
  14. @interface UnityAppController ()
  15. - (void)updateAppOrientation:(UIInterfaceOrientation)orientation;
  16. @end
  17. #ifndef __IPHONE_16_0
  18. @interface UIViewController ()
  19. - (void)setNeedsUpdateOfSupportedInterfaceOrientations;
  20. @end
  21. #endif
  22. @implementation UnityViewControllerBase (iOS)
  23. - (BOOL)shouldAutorotate
  24. {
  25. return YES;
  26. }
  27. - (BOOL)prefersStatusBarHidden
  28. {
  29. static bool _PrefersStatusBarHidden = true;
  30. static bool _PrefersStatusBarHiddenInited = false;
  31. if (!_PrefersStatusBarHiddenInited)
  32. {
  33. NSNumber* hidden = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"UIStatusBarHidden"];
  34. _PrefersStatusBarHidden = hidden ? [hidden boolValue] : YES;
  35. _PrefersStatusBarHiddenInited = true;
  36. }
  37. return _PrefersStatusBarHidden;
  38. }
  39. - (UIStatusBarStyle)preferredStatusBarStyle
  40. {
  41. static UIStatusBarStyle _PreferredStatusBarStyle = UIStatusBarStyleDefault;
  42. static bool _PreferredStatusBarStyleInited = false;
  43. if (!_PreferredStatusBarStyleInited)
  44. {
  45. NSString* style = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"UIStatusBarStyle"];
  46. if (style && [style isEqualToString: @"UIStatusBarStyleLightContent"])
  47. _PreferredStatusBarStyle = UIStatusBarStyleLightContent;
  48. if (style && [style isEqualToString: @"UIStatusBarStyleDarkContent"])
  49. _PreferredStatusBarStyle = UIStatusBarStyleDarkContent;
  50. _PreferredStatusBarStyleInited = true;
  51. }
  52. return _PreferredStatusBarStyle;
  53. }
  54. - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
  55. {
  56. UIRectEdge res = UIRectEdgeNone;
  57. if (UnityGetDeferSystemGesturesTopEdge())
  58. res |= UIRectEdgeTop;
  59. if (UnityGetDeferSystemGesturesBottomEdge())
  60. res |= UIRectEdgeBottom;
  61. if (UnityGetDeferSystemGesturesLeftEdge())
  62. res |= UIRectEdgeLeft;
  63. if (UnityGetDeferSystemGesturesRightEdge())
  64. res |= UIRectEdgeRight;
  65. return res;
  66. }
  67. - (BOOL)prefersHomeIndicatorAutoHidden
  68. {
  69. return UnityGetHideHomeButton();
  70. }
  71. @end
  72. @implementation UnityDefaultViewController
  73. {
  74. // these will be updated in one place where we "sync" UI side orientation handling to unity side
  75. NSUInteger _supportedOrientations;
  76. }
  77. - (id)init
  78. {
  79. if ((self = [super init]))
  80. {
  81. NSAssert(UnityShouldAutorotate(), @"UnityDefaultViewController should be used only if unity is set to autorotate");
  82. _supportedOrientations = EnabledAutorotationInterfaceOrientations();
  83. }
  84. return self;
  85. }
  86. - (void)updateSupportedOrientations
  87. {
  88. _supportedOrientations = EnabledAutorotationInterfaceOrientations();
  89. if (@available(iOS 16.0, *))
  90. [self setNeedsUpdateOfSupportedInterfaceOrientations];
  91. }
  92. - (NSUInteger)supportedInterfaceOrientations
  93. {
  94. return _supportedOrientations;
  95. }
  96. - (void)viewWillAppear:(BOOL)animated
  97. {
  98. ScreenOrientation currentOrientation = UIViewControllerOrientation(self);
  99. [GetAppController() updateAppOrientation: ConvertToIosScreenOrientation(currentOrientation)];
  100. [super viewWillAppear: animated];
  101. }
  102. - (void)viewDidAppear:(BOOL)animated
  103. {
  104. ScreenOrientation currentOrientation = UIViewControllerOrientation(self);
  105. [GetAppController() updateAppOrientation: ConvertToIosScreenOrientation(currentOrientation)];
  106. [super viewDidAppear: animated];
  107. }
  108. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
  109. {
  110. // CODE ARCHEOLOGY: we were using UIViewControllerOrientation, but on showing view with "Requires full screen"
  111. // CODE ARCHEOLOGY: we will get the size/orientation *already* set, and the rotation logic would break
  112. const ScreenOrientation curOrient = GetAppController().unityView.contentOrientation;
  113. const ScreenOrientation newOrient = OrientationAfterTransform(curOrient, [coordinator targetTransform]);
  114. // delay resolution change, ideally we want it to happen in the middle of rotation animation
  115. // we force rendering back upon completion, just in case transition happens sooner
  116. GetAppController().unityView.skipRendering = YES;
  117. [GetAppController().unityView performSelector: @selector(resumeRendering) withObject: nil afterDelay: REORIENTATION_RENDERING_PAUSE];
  118. // in case of presentation controller it will take control over orientations
  119. // so to avoid crazy corner cases, make default view controller to ignore "wrong" orientations
  120. // as they will come only in case of presentation view controller and will be reverted anyway
  121. // NB: we still want to pass message to super, we just want to skip unity-specific magic
  122. NSUInteger targetMask = 1 << ConvertToIosScreenOrientation(newOrient);
  123. if (([self supportedInterfaceOrientations] & targetMask) != 0)
  124. {
  125. [UIView setAnimationsEnabled: UnityUseAnimatedAutorotation() ? YES : NO];
  126. [KeyboardDelegate StartReorientation];
  127. [GetAppController() interfaceWillChangeOrientationTo: ConvertToIosScreenOrientation(newOrient)];
  128. [coordinator animateAlongsideTransition: nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
  129. [self.view setNeedsLayout];
  130. [GetAppController() interfaceDidChangeOrientationFrom: ConvertToIosScreenOrientation(curOrient)];
  131. [KeyboardDelegate FinishReorientation];
  132. [UIView setAnimationsEnabled: YES];
  133. GetAppController().unityView.skipRendering = NO;
  134. }];
  135. }
  136. [super viewWillTransitionToSize: size withTransitionCoordinator: coordinator];
  137. }
  138. @end
  139. @interface UnityFixedOrientationViewController()
  140. {
  141. UIInterfaceOrientation _fixedOrientation;
  142. }
  143. @end
  144. @implementation UnityFixedOrientationViewController
  145. - (instancetype)initWithOrientation:(UIInterfaceOrientation)interfaceOrientation
  146. {
  147. self = [super init];
  148. if (self) {
  149. _fixedOrientation = interfaceOrientation;
  150. }
  151. return self;
  152. }
  153. - (NSUInteger)supportedInterfaceOrientations
  154. {
  155. return 1 << _fixedOrientation;
  156. }
  157. - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
  158. {
  159. return _fixedOrientation;
  160. }
  161. - (void)viewWillAppear:(BOOL)animated
  162. {
  163. [GetAppController() updateAppOrientation: _fixedOrientation];
  164. [super viewWillAppear: animated];
  165. }
  166. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
  167. {
  168. // Adding a call to notify about changed orientation. In iOS16 there was a runtime change where view controller
  169. // is not fully set up as soon as we make the view controller change in AppController at -transitionToViewController:.
  170. // And instead iOS calls this method, in other cases this method is not called. So we want to call
  171. // didTransitionToViewController here as this is the place where we get updated orientation.
  172. //
  173. // NB: Look for additional explanation at UnityAppController+ViewHandling.mm method -transitionToViewController: before
  174. // call to same method.
  175. [GetAppController() didTransitionToViewController:self fromViewController:self];
  176. [super viewWillTransitionToSize: size withTransitionCoordinator: coordinator];
  177. }
  178. @end
  179. @implementation UnityPortraitOnlyViewController
  180. - (instancetype)init
  181. {
  182. self = [super initWithOrientation:UIInterfaceOrientationPortrait];
  183. return self;
  184. }
  185. @end
  186. @implementation UnityPortraitUpsideDownOnlyViewController
  187. - (instancetype)init
  188. {
  189. self = [super initWithOrientation:UIInterfaceOrientationPortraitUpsideDown];
  190. return self;
  191. }
  192. @end
  193. @implementation UnityLandscapeLeftOnlyViewController
  194. - (instancetype)init
  195. {
  196. self = [super initWithOrientation:UIInterfaceOrientationLandscapeLeft];
  197. return self;
  198. }
  199. @end
  200. @implementation UnityLandscapeRightOnlyViewController
  201. - (instancetype)init
  202. {
  203. self = [super initWithOrientation:UIInterfaceOrientationLandscapeRight];
  204. return self;
  205. }
  206. @end
  207. NSUInteger EnabledAutorotationInterfaceOrientations()
  208. {
  209. NSUInteger ret = 0;
  210. if (UnityIsOrientationEnabled(portrait))
  211. ret |= (1 << UIInterfaceOrientationPortrait);
  212. if (UnityDeviceSupportsUpsideDown() && UnityIsOrientationEnabled(portraitUpsideDown))
  213. ret |= (1 << UIInterfaceOrientationPortraitUpsideDown);
  214. if (UnityIsOrientationEnabled(landscapeLeft))
  215. ret |= (1 << UIInterfaceOrientationLandscapeRight);
  216. if (UnityIsOrientationEnabled(landscapeRight))
  217. ret |= (1 << UIInterfaceOrientationLandscapeLeft);
  218. // Handling unexpected case where autorotation is on and all the orientations are off by defaulting to current orientation.
  219. // Previously we returned 0 and iOS were handling it by keeping orientation as is. From iOS16 behaviour changed and the bug was raised.
  220. // Either way iOS requires us to provide non 0 value to supportedInterfaceOrientations.
  221. if (ret == 0)
  222. {
  223. NSLog(@"[Error] All orientations are off for autorotation. Preventing crash by using current orientation.");
  224. ret = (1 << [GetAppController() interfaceOrientation]);
  225. }
  226. return ret;
  227. }
  228. #endif // PLATFORM_IOS