UnityViewControllerBase+iOS.mm 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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. typedef id (*WillRotateToInterfaceOrientationSendFunc)(struct objc_super*, SEL, UIInterfaceOrientation, NSTimeInterval);
  12. static void WillRotateToInterfaceOrientation_DefaultImpl(id self_, SEL _cmd, UIInterfaceOrientation toInterfaceOrientation, NSTimeInterval duration);
  13. typedef id (*DidRotateFromInterfaceOrientationSendFunc)(struct objc_super*, SEL, UIInterfaceOrientation);
  14. static void DidRotateFromInterfaceOrientation_DefaultImpl(id self_, SEL _cmd, UIInterfaceOrientation fromInterfaceOrientation);
  15. typedef id (*ViewWillTransitionToSizeSendFunc)(struct objc_super*, SEL, CGSize, id<UIViewControllerTransitionCoordinator>);
  16. static void ViewWillTransitionToSize_DefaultImpl(id self_, SEL _cmd, CGSize size, id<UIViewControllerTransitionCoordinator> coordinator);
  17. // when returning from presenting UIViewController we might need to update app orientation to "correct" one, as we wont get rotation notification
  18. @interface UnityAppController ()
  19. - (void)updateAppOrientation:(UIInterfaceOrientation)orientation;
  20. @end
  21. @implementation UnityViewControllerBase (iOS)
  22. - (BOOL)shouldAutorotate
  23. {
  24. return YES;
  25. }
  26. - (BOOL)prefersStatusBarHidden
  27. {
  28. static bool _PrefersStatusBarHidden = true;
  29. static bool _PrefersStatusBarHiddenInited = false;
  30. if (!_PrefersStatusBarHiddenInited)
  31. {
  32. NSNumber* hidden = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"UIStatusBarHidden"];
  33. _PrefersStatusBarHidden = hidden ? [hidden boolValue] : YES;
  34. _PrefersStatusBarHiddenInited = true;
  35. }
  36. return _PrefersStatusBarHidden;
  37. }
  38. - (UIStatusBarStyle)preferredStatusBarStyle
  39. {
  40. static UIStatusBarStyle _PreferredStatusBarStyle = UIStatusBarStyleDefault;
  41. static bool _PreferredStatusBarStyleInited = false;
  42. if (!_PreferredStatusBarStyleInited)
  43. {
  44. NSString* style = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"UIStatusBarStyle"];
  45. if (style && [style isEqualToString: @"UIStatusBarStyleLightContent"])
  46. _PreferredStatusBarStyle = UIStatusBarStyleLightContent;
  47. _PreferredStatusBarStyleInited = true;
  48. }
  49. return _PreferredStatusBarStyle;
  50. }
  51. - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
  52. {
  53. UIRectEdge res = UIRectEdgeNone;
  54. if (UnityGetDeferSystemGesturesTopEdge())
  55. res |= UIRectEdgeTop;
  56. if (UnityGetDeferSystemGesturesBottomEdge())
  57. res |= UIRectEdgeBottom;
  58. if (UnityGetDeferSystemGesturesLeftEdge())
  59. res |= UIRectEdgeLeft;
  60. if (UnityGetDeferSystemGesturesRightEdge())
  61. res |= UIRectEdgeRight;
  62. return res;
  63. }
  64. - (BOOL)prefersHomeIndicatorAutoHidden
  65. {
  66. return UnityGetHideHomeButton();
  67. }
  68. @end
  69. @implementation UnityDefaultViewController
  70. - (NSUInteger)supportedInterfaceOrientations
  71. {
  72. NSAssert(UnityShouldAutorotate(), @"UnityDefaultViewController should be used only if unity is set to autorotate");
  73. return EnabledAutorotationInterfaceOrientations();
  74. }
  75. @end
  76. @implementation UnityPortraitOnlyViewController
  77. - (NSUInteger)supportedInterfaceOrientations
  78. {
  79. return 1 << UIInterfaceOrientationPortrait;
  80. }
  81. - (void)viewWillAppear:(BOOL)animated
  82. {
  83. [GetAppController() updateAppOrientation: UIInterfaceOrientationPortrait];
  84. [super viewWillAppear: animated];
  85. }
  86. @end
  87. @implementation UnityPortraitUpsideDownOnlyViewController
  88. - (NSUInteger)supportedInterfaceOrientations
  89. {
  90. return 1 << UIInterfaceOrientationPortraitUpsideDown;
  91. }
  92. - (void)viewWillAppear:(BOOL)animated
  93. {
  94. [GetAppController() updateAppOrientation: UIInterfaceOrientationPortraitUpsideDown];
  95. [super viewWillAppear: animated];
  96. }
  97. @end
  98. @implementation UnityLandscapeLeftOnlyViewController
  99. - (NSUInteger)supportedInterfaceOrientations
  100. {
  101. return 1 << UIInterfaceOrientationLandscapeLeft;
  102. }
  103. - (void)viewWillAppear:(BOOL)animated
  104. {
  105. [GetAppController() updateAppOrientation: UIInterfaceOrientationLandscapeLeft];
  106. [super viewWillAppear: animated];
  107. }
  108. @end
  109. @implementation UnityLandscapeRightOnlyViewController
  110. - (NSUInteger)supportedInterfaceOrientations
  111. {
  112. return 1 << UIInterfaceOrientationLandscapeRight;
  113. }
  114. - (void)viewWillAppear:(BOOL)animated
  115. {
  116. [GetAppController() updateAppOrientation: UIInterfaceOrientationLandscapeRight];
  117. [super viewWillAppear: animated];
  118. }
  119. @end
  120. // ios8 changed the way ViewController should handle rotation, so pick correct implementation at runtime
  121. static void WillRotateToInterfaceOrientation_DefaultImpl(id self_, SEL _cmd, UIInterfaceOrientation toInterfaceOrientation, NSTimeInterval duration)
  122. {
  123. [UIView setAnimationsEnabled: UnityUseAnimatedAutorotation() ? YES : NO];
  124. [GetAppController() interfaceWillChangeOrientationTo: toInterfaceOrientation];
  125. [KeyboardDelegate StartReorientation];
  126. AppController_SendUnityViewControllerNotification(kUnityInterfaceWillChangeOrientation);
  127. UNITY_OBJC_FORWARD_TO_SUPER(self_, [UIViewController class], @selector(willRotateToInterfaceOrientation:duration:), WillRotateToInterfaceOrientationSendFunc, toInterfaceOrientation, duration);
  128. }
  129. static void DidRotateFromInterfaceOrientation_DefaultImpl(id self_, SEL _cmd, UIInterfaceOrientation fromInterfaceOrientation)
  130. {
  131. UIViewController* self = (UIViewController*)self_;
  132. [self.view layoutSubviews];
  133. [GetAppController() interfaceDidChangeOrientationFrom: fromInterfaceOrientation];
  134. [KeyboardDelegate FinishReorientation];
  135. [UIView setAnimationsEnabled: YES];
  136. AppController_SendUnityViewControllerNotification(kUnityInterfaceDidChangeOrientation);
  137. UNITY_OBJC_FORWARD_TO_SUPER(self_, [UIViewController class], @selector(didRotateFromInterfaceOrientation:), DidRotateFromInterfaceOrientationSendFunc, fromInterfaceOrientation);
  138. }
  139. static void ViewWillTransitionToSize_DefaultImpl(id self_, SEL _cmd, CGSize size, id<UIViewControllerTransitionCoordinator> coordinator)
  140. {
  141. UIViewController* self = (UIViewController*)self_;
  142. ScreenOrientation curOrient = UIViewControllerOrientation(self);
  143. ScreenOrientation newOrient = OrientationAfterTransform(curOrient, [coordinator targetTransform]);
  144. // in case of presentation controller it will take control over orientations
  145. // so to avoid crazy corner cases, make default view controller to ignore "wrong" orientations
  146. // as they will come only in case of presentation view controller and will be reverted anyway
  147. // NB: we still want to pass message to super, we just want to skip unity-specific magic
  148. NSUInteger targetMask = 1 << ConvertToIosScreenOrientation(newOrient);
  149. if (([self supportedInterfaceOrientations] & targetMask) != 0)
  150. {
  151. [UIView setAnimationsEnabled: UnityUseAnimatedAutorotation() ? YES : NO];
  152. [KeyboardDelegate StartReorientation];
  153. [GetAppController() interfaceWillChangeOrientationTo: ConvertToIosScreenOrientation(newOrient)];
  154. [coordinator animateAlongsideTransition: nil completion:^(id < UIViewControllerTransitionCoordinatorContext > context) {
  155. [self.view setNeedsLayout];
  156. [GetAppController() interfaceDidChangeOrientationFrom: ConvertToIosScreenOrientation(curOrient)];
  157. [KeyboardDelegate FinishReorientation];
  158. [UIView setAnimationsEnabled: YES];
  159. }];
  160. }
  161. UNITY_OBJC_FORWARD_TO_SUPER(self_, [UIViewController class], @selector(viewWillTransitionToSize:withTransitionCoordinator:), ViewWillTransitionToSizeSendFunc, size, coordinator);
  162. }
  163. extern "C" void AddViewControllerRotationHandling(Class class_, IMP willRotateToInterfaceOrientation, IMP didRotateFromInterfaceOrientation, IMP viewWillTransitionToSize)
  164. {
  165. // it is important to use class_addMethod as we absolutely dont want to change super class impl (but rather just add override)
  166. if (_ios80orNewer && viewWillTransitionToSize)
  167. {
  168. class_addMethod(class_, @selector(viewWillTransitionToSize:withTransitionCoordinator:), viewWillTransitionToSize, UIViewController_viewWillTransitionToSize_Enc);
  169. }
  170. else
  171. {
  172. class_addMethod(class_, @selector(willRotateToInterfaceOrientation:duration:), willRotateToInterfaceOrientation, UIViewController_willRotateToInterfaceOrientation_Enc);
  173. class_addMethod(class_, @selector(didRotateFromInterfaceOrientation:), didRotateFromInterfaceOrientation, UIViewController_didRotateFromInterfaceOrientation_Enc);
  174. }
  175. }
  176. extern "C" void AddViewControllerDefaultRotationHandling(Class class_)
  177. {
  178. AddViewControllerRotationHandling(
  179. class_,
  180. (IMP)&WillRotateToInterfaceOrientation_DefaultImpl, (IMP)&DidRotateFromInterfaceOrientation_DefaultImpl,
  181. (IMP)&ViewWillTransitionToSize_DefaultImpl
  182. );
  183. }
  184. NSUInteger EnabledAutorotationInterfaceOrientations()
  185. {
  186. NSUInteger ret = 0;
  187. if (UnityIsOrientationEnabled(portrait))
  188. ret |= (1 << UIInterfaceOrientationPortrait);
  189. if (UnityIsOrientationEnabled(portraitUpsideDown))
  190. ret |= (1 << UIInterfaceOrientationPortraitUpsideDown);
  191. if (UnityIsOrientationEnabled(landscapeLeft))
  192. ret |= (1 << UIInterfaceOrientationLandscapeRight);
  193. if (UnityIsOrientationEnabled(landscapeRight))
  194. ret |= (1 << UIInterfaceOrientationLandscapeLeft);
  195. return ret;
  196. }
  197. #endif // PLATFORM_IOS