UnityAppController.mm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. #import "UnityAppController.h"
  2. #import "UnityAppController+ViewHandling.h"
  3. #import "UnityAppController+Rendering.h"
  4. #import "iPhone_Sensors.h"
  5. #import <CoreGraphics/CoreGraphics.h>
  6. #import <QuartzCore/QuartzCore.h>
  7. #import <QuartzCore/CADisplayLink.h>
  8. #import <Availability.h>
  9. #import <OpenGLES/EAGL.h>
  10. #import <OpenGLES/EAGLDrawable.h>
  11. #import <OpenGLES/ES2/gl.h>
  12. #import <OpenGLES/ES2/glext.h>
  13. #include <mach/mach_time.h>
  14. // MSAA_DEFAULT_SAMPLE_COUNT was moved to iPhone_GlesSupport.h
  15. // ENABLE_INTERNAL_PROFILER and related defines were moved to iPhone_Profiler.h
  16. // kFPS define for removed: you can use Application.targetFrameRate (30 fps by default)
  17. // DisplayLink is the only run loop mode now - all others were removed
  18. #include "CrashReporter.h"
  19. #include "UI/OrientationSupport.h"
  20. #include "UI/UnityView.h"
  21. #include "UI/Keyboard.h"
  22. #include "UI/SplashScreen.h"
  23. #include "Unity/InternalProfiler.h"
  24. #include "Unity/DisplayManager.h"
  25. #include "Unity/EAGLContextHelper.h"
  26. #include "Unity/GlesHelper.h"
  27. #include "Unity/ObjCRuntime.h"
  28. #include "PluginBase/AppDelegateListener.h"
  29. #include <assert.h>
  30. #include <stdbool.h>
  31. #include <sys/types.h>
  32. #include <unistd.h>
  33. #include <sys/sysctl.h>
  34. #import <FitfunSDK/FitfunSDK.h>
  35. #import "IOSPlatformSDK.h"
  36. #import <Bugly/Bugly.h>
  37. //
  38. UnityAppController* _UnityAppController = nil;
  39. // Standard Gesture Recognizers enabled on all iOS apps absorb touches close to the top and bottom of the screen.
  40. // This sometimes causes an ~1 second delay before the touch is handled when clicking very close to the edge.
  41. // You should enable this if you want to avoid that delay. Enabling it should not have any effect on default iOS gestures.
  42. #define DISABLE_TOUCH_DELAYS 1
  43. // we keep old bools around to support "old" code that might have used them
  44. bool _ios42orNewer = false, _ios43orNewer = false, _ios50orNewer = false, _ios60orNewer = false, _ios70orNewer = false;
  45. bool _ios80orNewer = false, _ios81orNewer = false, _ios82orNewer = false, _ios83orNewer = false, _ios90orNewer = false, _ios91orNewer = false;
  46. bool _ios100orNewer = false, _ios101orNewer = false, _ios102orNewer = false, _ios103orNewer = false;
  47. bool _ios110orNewer = false, _ios111orNewer = false, _ios112orNewer = false;
  48. // was unity rendering already inited: we should not touch rendering while this is false
  49. bool _renderingInited = false;
  50. // was unity inited: we should not touch unity api while this is false
  51. bool _unityAppReady = false;
  52. // see if there's a need to do internal player pause/resume handling
  53. //
  54. // Typically the trampoline code should manage this internally, but
  55. // there are use cases, videoplayer, plugin code, etc where the player
  56. // is paused before the internal handling comes relevant. Avoid
  57. // overriding externally managed player pause/resume handling by
  58. // caching the state
  59. bool _wasPausedExternal = false;
  60. // should we skip present on next draw: used in corner cases (like rotation) to fill both draw-buffers with some content
  61. bool _skipPresent = false;
  62. // was app "resigned active": some operations do not make sense while app is in background
  63. bool _didResignActive = false;
  64. // was startUnity scheduled: used to make startup robust in case of locking device
  65. static bool _startUnityScheduled = false;
  66. bool _supportsMSAA = false;
  67. #if UNITY_SUPPORT_ROTATION
  68. // Required to enable specific orientation for some presentation controllers: see supportedInterfaceOrientationsForWindow below for details
  69. NSInteger _forceInterfaceOrientationMask = 0;
  70. #endif
  71. @implementation UnityAppController
  72. @synthesize unityView = _unityView;
  73. @synthesize unityDisplayLink = _displayLink;
  74. @synthesize rootView = _rootView;
  75. @synthesize rootViewController = _rootController;
  76. @synthesize mainDisplay = _mainDisplay;
  77. @synthesize renderDelegate = _renderDelegate;
  78. @synthesize quitHandler = _quitHandler;
  79. #if UNITY_SUPPORT_ROTATION
  80. @synthesize interfaceOrientation = _curOrientation;
  81. #endif
  82. - (id)init
  83. {
  84. if ((self = _UnityAppController = [super init]))
  85. {
  86. // due to clang issues with generating warning for overriding deprecated methods
  87. // we will simply assert if deprecated methods are present
  88. // NB: methods table is initied at load (before this call), so it is ok to check for override
  89. NSAssert(![self respondsToSelector: @selector(createUnityViewImpl)],
  90. @"createUnityViewImpl is deprecated and will not be called. Override createUnityView"
  91. );
  92. NSAssert(![self respondsToSelector: @selector(createViewHierarchyImpl)],
  93. @"createViewHierarchyImpl is deprecated and will not be called. Override willStartWithViewController"
  94. );
  95. NSAssert(![self respondsToSelector: @selector(createViewHierarchy)],
  96. @"createViewHierarchy is deprecated and will not be implemented. Use createUI"
  97. );
  98. }
  99. return self;
  100. }
  101. - (void)setWindow:(id)object {}
  102. - (UIWindow*)window { return _window; }
  103. - (void)shouldAttachRenderDelegate {}
  104. - (void)preStartUnity {}
  105. - (void)startUnity:(UIApplication*)application
  106. {
  107. NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");
  108. UnityInitApplicationGraphics();
  109. // we make sure that first level gets correct display list and orientation
  110. [[DisplayManager Instance] updateDisplayListInUnity];
  111. UnityLoadApplication();
  112. Profiler_InitProfiler();
  113. [self showGameUI];
  114. [self createDisplayLink];
  115. UnitySetPlayerFocus(1);
  116. }
  117. extern "C" void UnityDestroyDisplayLink()
  118. {
  119. [GetAppController() destroyDisplayLink];
  120. }
  121. extern "C" void UnityRequestQuit()
  122. {
  123. _didResignActive = true;
  124. if (GetAppController().quitHandler)
  125. GetAppController().quitHandler();
  126. else
  127. exit(0);
  128. }
  129. #if UNITY_SUPPORT_ROTATION
  130. - (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
  131. {
  132. // No rootViewController is set because we are switching from one view controller to another, all orientations should be enabled
  133. if ([window rootViewController] == nil)
  134. return UIInterfaceOrientationMaskAll;
  135. // Some presentation controllers (e.g. UIImagePickerController) require portrait orientation and will throw exception if it is not supported.
  136. // At the same time enabling all orientations by returning UIInterfaceOrientationMaskAll might cause unwanted orientation change
  137. // (e.g. when using UIActivityViewController to "share to" another application, iOS will use supportedInterfaceOrientations to possibly reorient).
  138. // So to avoid exception we are returning combination of constraints for root view controller and orientation requested by iOS.
  139. // _forceInterfaceOrientationMask is updated in willChangeStatusBarOrientation, which is called if some presentation controller insists on orientation change.
  140. return [[window rootViewController] supportedInterfaceOrientations] | _forceInterfaceOrientationMask;
  141. }
  142. - (void)application:(UIApplication*)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration
  143. {
  144. // Setting orientation mask which is requiested by iOS: see supportedInterfaceOrientationsForWindow above for details
  145. _forceInterfaceOrientationMask = 1 << newStatusBarOrientation;
  146. }
  147. #endif
  148. #if !PLATFORM_TVOS
  149. - (void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification
  150. {
  151. AppController_SendNotificationWithArg(kUnityDidReceiveLocalNotification, notification);
  152. UnitySendLocalNotification(notification);
  153. }
  154. #endif
  155. #if UNITY_USES_REMOTE_NOTIFICATIONS
  156. - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
  157. {
  158. AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo);
  159. UnitySendRemoteNotification(userInfo);
  160. }
  161. - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
  162. {
  163. AppController_SendNotificationWithArg(kUnityDidRegisterForRemoteNotificationsWithDeviceToken, deviceToken);
  164. UnitySendDeviceToken(deviceToken);
  165. }
  166. #if !PLATFORM_TVOS
  167. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
  168. {
  169. AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo);
  170. UnitySendRemoteNotification(userInfo);
  171. if (handler)
  172. {
  173. handler(UIBackgroundFetchResultNoData);
  174. }
  175. }
  176. #endif
  177. - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
  178. {
  179. AppController_SendNotificationWithArg(kUnityDidFailToRegisterForRemoteNotificationsWithError, error);
  180. UnitySendRemoteNotificationError(error);
  181. // alas people do not check remote notification error through api (which is clunky, i agree) so log here to have at least some visibility
  182. ::printf("\nFailed to register for remote notifications:\n%s\n\n", [[error localizedDescription] UTF8String]);
  183. }
  184. #endif
  185. - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation
  186. {
  187. NSMutableArray* keys = [NSMutableArray arrayWithCapacity: 3];
  188. NSMutableArray* values = [NSMutableArray arrayWithCapacity: 3];
  189. #define ADD_ITEM(item) do{ if(item) {[keys addObject:@#item]; [values addObject:item];} }while(0)
  190. ADD_ITEM(url);
  191. ADD_ITEM(sourceApplication);
  192. ADD_ITEM(annotation);
  193. #undef ADD_ITEM
  194. NSDictionary* notifData = [NSDictionary dictionaryWithObjects: values forKeys: keys];
  195. AppController_SendNotificationWithArg(kUnityOnOpenURL, notifData);
  196. // [FitfunSDKManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
  197. IOSPlatformSDK * sdk = [IOSPlatformSDK sharedInstance];
  198. [sdk startWithUrl:url];
  199. return YES;
  200. }
  201. - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions
  202. {
  203. return YES;
  204. }
  205. - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
  206. {
  207. ::printf("-> applicationDidFinishLaunching()\n");
  208. // send notfications
  209. #if !PLATFORM_TVOS
  210. if (UILocalNotification* notification = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey])
  211. UnitySendLocalNotification(notification);
  212. if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
  213. [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
  214. #endif
  215. UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]);
  216. [self selectRenderingAPI];
  217. [UnityRenderingView InitializeForAPI: self.renderingAPI];
  218. _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
  219. _unityView = [self createUnityView];
  220. [DisplayManager Initialize];
  221. _mainDisplay = [DisplayManager Instance].mainDisplay;
  222. [_mainDisplay createWithWindow: _window andView: _unityView];
  223. [self createUI];
  224. [self preStartUnity];
  225. // if you wont use keyboard you may comment it out at save some memory
  226. [KeyboardDelegate Initialize];
  227. #if !PLATFORM_TVOS && DISABLE_TOUCH_DELAYS
  228. for (UIGestureRecognizer *g in _window.gestureRecognizers)
  229. {
  230. g.delaysTouchesBegan = false;
  231. }
  232. #endif
  233. //注册初始化
  234. [[FitfunSDKManager ff_sharedInstance] ff_registerSDK];
  235. // [FitfunSDKManager application:application didFinishLaunchingWithOptions:launchOptions];
  236. // 以下是几种打开App方式
  237. if(launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]){
  238. NSLog(@"打开方式 远程推送打开");
  239. }else if(launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]){
  240. NSLog(@"打开方式 本地推送打开");
  241. }else if(launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey]){
  242. NSLog(@"打开方式 UniversalLinks打开");
  243. }else if(launchOptions[UIApplicationLaunchOptionsURLKey]){
  244. // 我们需要在此处处理通过 scheme 打开App并截获参数。
  245. NSURL *url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
  246. NSLog(@"打开方式 通过URL打开的 ===== >> %@",url);
  247. IOSPlatformSDK * sdk = [IOSPlatformSDK sharedInstance];
  248. sdk.openStyle = @"启动前 通过URL打开的";
  249. [sdk startWithUrl:url];
  250. }else if (!launchOptions){
  251. NSLog(@"打开方式 手动点击打开");
  252. IOSPlatformSDK * sdk = [IOSPlatformSDK sharedInstance];
  253. sdk.openStyle = @"手动打开";
  254. }
  255. [Bugly startWithAppId:@"403d275730"];
  256. return YES;
  257. }
  258. - (void)applicationDidEnterBackground:(UIApplication*)application{
  259. ::printf("-> applicationDidEnterBackground()\n");
  260. //开启后台任务保活
  261. BTDataProcess * sharedInstance = [BTDataProcess sharedInstance];
  262. sharedInstance.isBackGround = YES;
  263. //开启后台任务保活
  264. // [self comeToBackgroundMode];
  265. }
  266. - (void)applicationWillEnterForeground:(UIApplication*)application
  267. {
  268. ::printf("-> applicationWillEnterForeground()\n");
  269. BTDataProcess * sharedInstance = [BTDataProcess sharedInstance];
  270. sharedInstance.isBackGround = NO;
  271. [sharedInstance applicationWillEnterForeground];
  272. // applicationWillEnterForeground: might sometimes arrive *before* actually initing unity (e.g. locking on startup)
  273. if (_unityAppReady)
  274. {
  275. // if we were showing video before going to background - the view size may be changed while we are in background
  276. [GetAppController().unityView recreateRenderingSurfaceIfNeeded];
  277. }
  278. }
  279. - (void)applicationDidBecomeActive:(UIApplication*)application
  280. {
  281. ::printf("-> applicationDidBecomeActive()\n");
  282. [self removeSnapshotView];
  283. if (_unityAppReady)
  284. {
  285. if (UnityIsPaused() && _wasPausedExternal == false)
  286. {
  287. UnityWillResume();
  288. UnityPause(0);
  289. }
  290. if (_wasPausedExternal)
  291. {
  292. if (UnityIsFullScreenPlaying())
  293. TryResumeFullScreenVideo();
  294. }
  295. UnitySetPlayerFocus(1);
  296. }
  297. else if (!_startUnityScheduled)
  298. {
  299. _startUnityScheduled = true;
  300. [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
  301. }
  302. _didResignActive = false;
  303. }
  304. - (void)removeSnapshotView
  305. {
  306. // do this on the main queue async so that if we try to create one
  307. // and remove in the same frame, this always happens after in the same queue
  308. dispatch_async(dispatch_get_main_queue(), ^{
  309. if (_snapshotView)
  310. {
  311. [_snapshotView removeFromSuperview];
  312. _snapshotView = nil;
  313. // Make sure that the keyboard input field regains focus after the application becomes active.
  314. [[KeyboardDelegate Instance] becomeFirstResponder];
  315. }
  316. });
  317. }
  318. - (void)applicationWillResignActive:(UIApplication*)application
  319. {
  320. ::printf("-> applicationWillResignActive()\n");
  321. if (_unityAppReady)
  322. {
  323. UnitySetPlayerFocus(0);
  324. _wasPausedExternal = UnityIsPaused();
  325. if (_wasPausedExternal == false)
  326. {
  327. // Pause Unity only if we don't need special background processing
  328. // otherwise batched player loop can be called to run user scripts.
  329. if (!UnityGetUseCustomAppBackgroundBehavior())
  330. {
  331. // Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager.
  332. // NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop)
  333. // NB: We will actually pause after the loop (when calling UnityPause).
  334. UnityWillPause();
  335. [self repaint];
  336. UnityPause(1);
  337. // this is done on the next frame so that
  338. // in the case where unity is paused while going
  339. // into the background and an input is deactivated
  340. // we don't mess with the view hierarchy while taking
  341. // a view snapshot (case 760747).
  342. dispatch_async(dispatch_get_main_queue(), ^{
  343. // if we are active again, we don't need to do this anymore
  344. if (!_didResignActive)
  345. {
  346. return;
  347. }
  348. _snapshotView = [self createSnapshotView];
  349. if (_snapshotView)
  350. [_rootView addSubview: _snapshotView];
  351. });
  352. }
  353. }
  354. }
  355. _didResignActive = true;
  356. }
  357. - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
  358. {
  359. ::printf("WARNING -> applicationDidReceiveMemoryWarning()\n");
  360. UnityLowMemory();
  361. }
  362. - (void)applicationWillTerminate:(UIApplication*)application
  363. {
  364. ::printf("-> applicationWillTerminate()\n");
  365. Profiler_UninitProfiler();
  366. UnityCleanup();
  367. extern void SensorsCleanup();
  368. SensorsCleanup();
  369. }
  370. @end
  371. void AppController_SendNotification(NSString* name)
  372. {
  373. [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController()];
  374. }
  375. void AppController_SendNotificationWithArg(NSString* name, id arg)
  376. {
  377. [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController() userInfo: arg];
  378. }
  379. void AppController_SendUnityViewControllerNotification(NSString* name)
  380. {
  381. [[NSNotificationCenter defaultCenter] postNotificationName: name object: UnityGetGLViewController()];
  382. }
  383. extern "C" UIWindow* UnityGetMainWindow() {
  384. return GetAppController().mainDisplay.window;
  385. }
  386. extern "C" UIViewController* UnityGetGLViewController() {
  387. return GetAppController().rootViewController;
  388. }
  389. extern "C" UIView* UnityGetGLView() {
  390. return GetAppController().unityView;
  391. }
  392. extern "C" ScreenOrientation UnityCurrentOrientation() { return GetAppController().unityView.contentOrientation; }
  393. bool LogToNSLogHandler(LogType logType, const char* log, va_list list)
  394. {
  395. NSLogv([NSString stringWithUTF8String: log], list);
  396. return true;
  397. }
  398. static void AddNewAPIImplIfNeeded();
  399. // From https://stackoverflow.com/questions/4744826/detecting-if-ios-app-is-run-in-debugger
  400. static bool isDebuggerAttachedToConsole(void)
  401. // Returns true if the current process is being debugged (either
  402. // running under the debugger or has a debugger attached post facto).
  403. {
  404. int junk;
  405. int mib[4];
  406. struct kinfo_proc info;
  407. size_t size;
  408. // Initialize the flags so that, if sysctl fails for some bizarre
  409. // reason, we get a predictable result.
  410. info.kp_proc.p_flag = 0;
  411. // Initialize mib, which tells sysctl the info we want, in this case
  412. // we're looking for information about a specific process ID.
  413. mib[0] = CTL_KERN;
  414. mib[1] = KERN_PROC;
  415. mib[2] = KERN_PROC_PID;
  416. mib[3] = getpid();
  417. // Call sysctl.
  418. size = sizeof(info);
  419. junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
  420. assert(junk == 0);
  421. // We're being debugged if the P_TRACED flag is set.
  422. return ((info.kp_proc.p_flag & P_TRACED) != 0);
  423. }
  424. void UnityInitTrampoline()
  425. {
  426. #if ENABLE_CRASH_REPORT_SUBMISSION
  427. SubmitCrashReportsAsync();
  428. #endif
  429. InitCrashHandling();
  430. _ios42orNewer = _ios43orNewer = _ios50orNewer = _ios60orNewer = _ios70orNewer = true;
  431. NSString* version = [[UIDevice currentDevice] systemVersion];
  432. #define CHECK_VER(s) [version compare: s options: NSNumericSearch] != NSOrderedAscending
  433. _ios80orNewer = CHECK_VER(@"8.0"), _ios81orNewer = CHECK_VER(@"8.1"), _ios82orNewer = CHECK_VER(@"8.2"), _ios83orNewer = CHECK_VER(@"8.3");
  434. _ios90orNewer = CHECK_VER(@"9.0"), _ios91orNewer = CHECK_VER(@"9.1");
  435. _ios100orNewer = CHECK_VER(@"10.0"), _ios101orNewer = CHECK_VER(@"10.1"), _ios102orNewer = CHECK_VER(@"10.2"), _ios103orNewer = CHECK_VER(@"10.3");
  436. _ios110orNewer = CHECK_VER(@"11.0"), _ios111orNewer = CHECK_VER(@"11.1"), _ios112orNewer = CHECK_VER(@"11.2");
  437. #undef CHECK_VER
  438. AddNewAPIImplIfNeeded();
  439. #if !TARGET_IPHONE_SIMULATOR
  440. // Use NSLog logging if a debugger is not attached, otherwise we write to stdout.
  441. if (!isDebuggerAttachedToConsole())
  442. UnitySetLogEntryHandler(LogToNSLogHandler);
  443. #endif
  444. }
  445. // sometimes apple adds new api with obvious fallback on older ios.
  446. // in that case we simply add these functions ourselves to simplify code
  447. static void AddNewAPIImplIfNeeded()
  448. {
  449. if (![[CADisplayLink class] instancesRespondToSelector: @selector(setPreferredFramesPerSecond:)])
  450. {
  451. IMP CADisplayLink_setPreferredFramesPerSecond_IMP = imp_implementationWithBlock(^void(id _self, NSInteger fps) {
  452. typedef void (*SetFrameIntervalFunc)(id, SEL, NSInteger);
  453. UNITY_OBJC_CALL_ON_SELF(_self, @selector(setFrameInterval:), SetFrameIntervalFunc, (int)(60.0f / fps));
  454. });
  455. class_replaceMethod([CADisplayLink class], @selector(setPreferredFramesPerSecond:), CADisplayLink_setPreferredFramesPerSecond_IMP, CADisplayLink_setPreferredFramesPerSecond_Enc);
  456. }
  457. if (![[UIScreen class] instancesRespondToSelector: @selector(nativeScale)])
  458. {
  459. IMP UIScreen_NativeScale_IMP = imp_implementationWithBlock(^CGFloat(id _self) {
  460. return ((UIScreen*)_self).scale;
  461. });
  462. class_replaceMethod([UIScreen class], @selector(nativeScale), UIScreen_NativeScale_IMP, UIScreen_nativeScale_Enc);
  463. }
  464. if (![[UIScreen class] instancesRespondToSelector: @selector(maximumFramesPerSecond)])
  465. {
  466. IMP UIScreen_MaximumFramesPerSecond_IMP = imp_implementationWithBlock(^NSInteger(id _self) {
  467. return 60;
  468. });
  469. class_replaceMethod([UIScreen class], @selector(maximumFramesPerSecond), UIScreen_MaximumFramesPerSecond_IMP, UIScreen_maximumFramesPerSecond_Enc);
  470. }
  471. if (![[UIView class] instancesRespondToSelector: @selector(safeAreaInsets)])
  472. {
  473. IMP UIView_SafeAreaInsets_IMP = imp_implementationWithBlock(^UIEdgeInsets(id _self) {
  474. return UIEdgeInsetsZero;
  475. });
  476. class_replaceMethod([UIView class], @selector(safeAreaInsets), UIView_SafeAreaInsets_IMP, UIView_safeAreaInsets_Enc);
  477. }
  478. }