SplashScreen.mm 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #include "SplashScreen.h"
  2. #include "UnityViewControllerBase.h"
  3. #include "OrientationSupport.h"
  4. #include "Unity/ObjCRuntime.h"
  5. #include "UI/UnityView.h"
  6. #include <cstring>
  7. #include "Unity/UnitySharedDecls.h"
  8. #include <utility>
  9. static SplashScreen* _splash = nil;
  10. static SplashScreenController* _controller = nil;
  11. @implementation SplashScreen
  12. {
  13. UIImageView* m_ImageView;
  14. UIView* m_XibView;
  15. }
  16. - (id)initWithFrame:(CGRect)frame
  17. {
  18. self = [super initWithFrame: frame];
  19. return self;
  20. }
  21. - (void)createUI
  22. {
  23. NSString* launchScreen = [[NSBundle mainBundle].infoDictionary[@"UILaunchStoryboardName"] stringByDeletingPathExtension];
  24. const bool hasXIB = [[NSBundle mainBundle] pathForResource: launchScreen ofType: @"nib"] != nil;
  25. if (hasXIB)
  26. {
  27. self->m_XibView = [[[NSBundle mainBundle] loadNibNamed: launchScreen owner: nil options: nil] objectAtIndex: 0];
  28. [self addSubview: self->m_XibView];
  29. }
  30. else
  31. {
  32. #if !PLATFORM_TVOS
  33. NSAssert(NO, @"no storyboard/xib was provided.");
  34. #endif
  35. // we still support launch images on tvos, but unlike iOS there are only two options and no orientations
  36. UIImage* launchImage = nil;
  37. if ([UIScreen mainScreen].scale > 1.0)
  38. launchImage = [UIImage imageNamed: @"LaunchImage@2x"];
  39. if (!launchImage)
  40. launchImage = [UIImage imageNamed: @"LaunchImage@"];
  41. self->m_ImageView = [[UIImageView alloc] initWithImage: launchImage];
  42. [self addSubview: self->m_ImageView];
  43. }
  44. }
  45. - (void)updateOrientation:(ScreenOrientation)orient withSupportedOrientations:(const OrientationMask&)supportedOrientations
  46. {
  47. CGFloat scale = UnityScreenScaleFactor([UIScreen mainScreen]);
  48. UnityReportResizeView((unsigned)(self.bounds.size.width * scale), (unsigned)(self.bounds.size.height * scale), orient);
  49. ReportSafeAreaChangeForView(self);
  50. // for iOS only xib/storyboard are supported, for tvOS (launch images are supported) no orientation takes place at all
  51. }
  52. - (void)layoutSubviews
  53. {
  54. if (self->m_XibView)
  55. self->m_XibView.frame = self.bounds;
  56. else if (self->m_ImageView)
  57. self->m_ImageView.frame = self.bounds;
  58. }
  59. + (SplashScreen*)Instance
  60. {
  61. return _splash;
  62. }
  63. - (void)freeSubviews
  64. {
  65. m_ImageView = nil;
  66. m_XibView = nil;
  67. }
  68. @end
  69. @implementation SplashScreenController
  70. {
  71. OrientationMask _supportedOrientations;
  72. }
  73. - (id)init
  74. {
  75. self = [super init];
  76. if (self)
  77. {
  78. self->_supportedOrientations = { false, false, false, false };
  79. }
  80. return self;
  81. }
  82. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
  83. {
  84. ScreenOrientation curOrient = UIViewControllerOrientation(self);
  85. ScreenOrientation newOrient = OrientationAfterTransform(curOrient, [coordinator targetTransform]);
  86. [_splash updateOrientation: newOrient withSupportedOrientations: self->_supportedOrientations];
  87. [super viewWillTransitionToSize: size withTransitionCoordinator: coordinator];
  88. }
  89. - (void)create:(UIWindow*)window
  90. {
  91. NSArray* supportedOrientation = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"UISupportedInterfaceOrientations"];
  92. // splash will be shown way before unity is inited so we need to override autorotation handling with values read from info.plist
  93. self->_supportedOrientations.portrait = [supportedOrientation containsObject: @"UIInterfaceOrientationPortrait"];
  94. self->_supportedOrientations.portraitUpsideDown = [supportedOrientation containsObject: @"UIInterfaceOrientationPortraitUpsideDown"];
  95. self->_supportedOrientations.landscapeLeft = [supportedOrientation containsObject: @"UIInterfaceOrientationLandscapeRight"];
  96. self->_supportedOrientations.landscapeRight = [supportedOrientation containsObject: @"UIInterfaceOrientationLandscapeLeft"];
  97. // special handling of devices/ios that do not support upside down orientation
  98. if (!UnityDeviceSupportsUpsideDown())
  99. {
  100. self->_supportedOrientations.portraitUpsideDown = false;
  101. OrientationMask om = self->_supportedOrientations;
  102. const bool anySupported = om.portrait || om.landscapeLeft || om.landscapeRight;
  103. if (!anySupported)
  104. {
  105. self->_supportedOrientations.portrait = true;
  106. printf_console("This device does not support UpsideDown orientation, so we switched to Portrait.\n");
  107. }
  108. }
  109. _splash = [[SplashScreen alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
  110. _splash.contentScaleFactor = UnityScreenScaleFactor([UIScreen mainScreen]);
  111. _splash.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  112. _splash.autoresizesSubviews = YES;
  113. [_splash createUI];
  114. window.rootViewController = self;
  115. self.view = _splash;
  116. [window addSubview: _splash];
  117. [window bringSubviewToFront: _splash];
  118. ScreenOrientation orient = UIViewControllerOrientation(self);
  119. [_splash updateOrientation: orient withSupportedOrientations: self->_supportedOrientations];
  120. OrientView([SplashScreenController Instance], _splash, orient);
  121. }
  122. #if PLATFORM_IOS
  123. - (BOOL)shouldAutorotate
  124. {
  125. return YES;
  126. }
  127. - (NSUInteger)supportedInterfaceOrientations
  128. {
  129. NSUInteger ret = 0;
  130. if (self->_supportedOrientations.portrait)
  131. ret |= (1 << UIInterfaceOrientationPortrait);
  132. if (self->_supportedOrientations.portraitUpsideDown)
  133. ret |= (1 << UIInterfaceOrientationPortraitUpsideDown);
  134. if (self->_supportedOrientations.landscapeLeft)
  135. ret |= (1 << UIInterfaceOrientationLandscapeRight);
  136. if (self->_supportedOrientations.landscapeRight)
  137. ret |= (1 << UIInterfaceOrientationLandscapeLeft);
  138. return ret;
  139. }
  140. #endif
  141. + (SplashScreenController*)Instance
  142. {
  143. return _controller;
  144. }
  145. @end
  146. void ShowSplashScreen(UIWindow* window)
  147. {
  148. NSString* launchScreen = [[NSBundle mainBundle].infoDictionary[@"UILaunchStoryboardName"] stringByDeletingPathExtension];
  149. #if PLATFORM_IOS
  150. // since launch images are no longer supported on ios we MUST have UILaunchStoryboardName filled
  151. assert(launchScreen != nil && "UILaunchStoryboardName key is missing from info.plist");
  152. #endif
  153. const bool hasStoryboard = launchScreen != nil && [[NSBundle mainBundle] pathForResource: launchScreen ofType: @"storyboardc"] != nil;
  154. if (hasStoryboard)
  155. {
  156. UIStoryboard *storyboard = [UIStoryboard storyboardWithName: launchScreen bundle: [NSBundle mainBundle]];
  157. // on ios13 we can finally tweak initial storyboard view controller: use unity base view controller
  158. // this way we can handle orientations/status-bar/whatever-we-want-to-tweak uniformly
  159. // as we still support xcode pre-11 we must do this weird dance of checking for both sdk and runtime version
  160. // otherwise it fails to compile (due to unknown selector)
  161. #if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
  162. if (@available(iOS 13.0, tvOS 13.0, *))
  163. {
  164. _controller = [storyboard instantiateInitialViewControllerWithCreator:^(NSCoder *coder) {
  165. return [AllocUnityViewController() initWithCoder: coder];
  166. }];
  167. }
  168. else
  169. #endif
  170. {
  171. _controller = [storyboard instantiateInitialViewController];
  172. }
  173. window.rootViewController = _controller;
  174. }
  175. else
  176. {
  177. _controller = [[SplashScreenController alloc] init];
  178. [_controller create: window];
  179. }
  180. [window makeKeyAndVisible];
  181. }
  182. void HideSplashScreen()
  183. {
  184. if (_splash)
  185. {
  186. [_splash removeFromSuperview];
  187. [_splash freeSubviews];
  188. }
  189. _splash = nil;
  190. _controller = nil;
  191. }