From d0a32c2011ca00991be45ac3fa320f4fc663b2e8 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Wed, 26 Feb 2020 19:52:05 -0800 Subject: [PATCH] iOS: Make RCTKeyWindow multi-window aware and add UIScene support to RCTRedBox (#28147) Summary: `RCTRedBox` doesn't appear in apps implementing `UISceneDelegate`. ## Changelog [iOS] [Changed] - `RCTKeyWindow()` is now multi-window aware [iOS] [Fixed] - `RCTRedBox` doesn't appear in apps implementing `UISceneDelegate` Pull Request resolved: https://github.com/facebook/react-native/pull/28147 Test Plan: - Trigger an error in RNTester - Trigger an error in an app implementing `UISceneDelegate` ![Simulator Screen Shot - iPhone 11 Pro Max - 2020-02-21 at 14 17 54](https://user-images.githubusercontent.com/4123478/75037702-14066a80-54b5-11ea-9373-b56b467be845.png) Reviewed By: PeteTheHeat Differential Revision: D20036399 Pulled By: hramos fbshipit-source-id: 07d83e985b02296f930114e3c7100c2077e82300 --- React/Base/RCTUtils.m | 7 ++++- React/CoreModules/RCTRedBox.mm | 53 ++++++++++++++++------------------ 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 540b05ac23f..7a5ef4e172f 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -497,7 +497,12 @@ UIWindow *__nullable RCTKeyWindow(void) } // TODO: replace with a more robust solution - return RCTSharedApplication().keyWindow; + for (UIWindow *window in RCTSharedApplication().windows) { + if (window.keyWindow) { + return window; + } + } + return nil; } UIViewController *__nullable RCTPresentedViewController(void) diff --git a/React/CoreModules/RCTRedBox.mm b/React/CoreModules/RCTRedBox.mm index a83fc0b59d4..c7133ac5522 100644 --- a/React/CoreModules/RCTRedBox.mm +++ b/React/CoreModules/RCTRedBox.mm @@ -70,7 +70,8 @@ @end -@interface RCTRedBoxWindow : UIWindow +@interface RCTRedBoxWindow : NSObject +@property (nonatomic, strong) UIViewController *rootViewController; @property (nonatomic, weak) id actionDelegate; @end @@ -82,22 +83,17 @@ int _lastErrorCookie; } -- (instancetype)initWithFrame:(CGRect)frame customButtonTitles:(NSArray*)customButtonTitles customButtonHandlers:(NSArray *)customButtonHandlers +- (instancetype)initWithFrame:(CGRect)frame + customButtonTitles:(NSArray*)customButtonTitles + customButtonHandlers:(NSArray *)customButtonHandlers { - _lastErrorCookie = -1; - if ((self = [super initWithFrame:frame])) { -#if TARGET_OS_TV - self.windowLevel = UIWindowLevelAlert + 1000; -#else - self.windowLevel = UIWindowLevelStatusBar - 1; -#endif - self.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1]; - self.hidden = YES; + if (self = [super init]) { + _lastErrorCookie = -1; - UIViewController *rootController = [UIViewController new]; - self.rootViewController = rootController; - UIView *rootView = rootController.view; - rootView.backgroundColor = [UIColor clearColor]; + _rootViewController = [UIViewController new]; + UIView *rootView = _rootViewController.view; + rootView.frame = frame; + rootView.backgroundColor = [UIColor blackColor]; const CGFloat buttonHeight = 60; @@ -133,8 +129,8 @@ UIButton *copyButton = [self redBoxButton:copyText accessibilityIdentifier:@"redbox-copy" selector:@selector(copyStack) block:nil]; UIButton *extraButton = [self redBoxButton:extraText accessibilityIdentifier:@"redbox-extra" selector:@selector(showExtraDataViewController) block:nil]; - CGFloat buttonWidth = self.bounds.size.width / (4 + [customButtonTitles count]); - CGFloat bottomButtonHeight = self.bounds.size.height - buttonHeight - [self bottomSafeViewHeight]; + CGFloat buttonWidth = frame.size.width / (4 + [customButtonTitles count]); + CGFloat bottomButtonHeight = frame.size.height - buttonHeight - [self bottomSafeViewHeight]; dismissButton.frame = CGRectMake(0, bottomButtonHeight, buttonWidth, buttonHeight); reloadButton.frame = CGRectMake(buttonWidth, bottomButtonHeight, buttonWidth, buttonHeight); copyButton.frame = CGRectMake(buttonWidth * 2, bottomButtonHeight, buttonWidth, buttonHeight); @@ -158,7 +154,7 @@ UIView *bottomSafeView = [UIView new]; bottomSafeView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1]; - bottomSafeView.frame = CGRectMake(0, self.bounds.size.height - [self bottomSafeViewHeight], self.bounds.size.width, [self bottomSafeViewHeight]); + bottomSafeView.frame = CGRectMake(0, frame.size.height - [self bottomSafeViewHeight], frame.size.width, [self bottomSafeViewHeight]); [rootView addSubview:bottomSafeView]; } @@ -209,15 +205,18 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) return [regex stringByReplacingMatchesInString:text options:0 range:NSMakeRange(0, [text length]) withTemplate:@""]; } -- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate errorCookie:(int)errorCookie +- (void)showErrorMessage:(NSString *)message + withStack:(NSArray *)stack + isUpdate:(BOOL)isUpdate + errorCookie:(int)errorCookie { // Remove ANSI color codes from the message NSString *messageWithoutAnsi = [self stripAnsi:message]; // Show if this is a new message, or if we're updating the previous message - BOOL isNew = self.hidden && !isUpdate; + BOOL isNew = !self.rootViewController.isBeingPresented && !isUpdate; BOOL isUpdateForSameMessage = !isNew && ( - !self.hidden && isUpdate && ( + self.rootViewController.isBeingPresented && isUpdate && ( (errorCookie == -1 && [_lastErrorMessage isEqualToString:messageWithoutAnsi]) || (errorCookie == _lastErrorCookie) ) @@ -231,22 +230,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) [_stackTraceTableView reloadData]; - if (self.hidden) { + if (!self.rootViewController.isBeingPresented) { [_stackTraceTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; + [RCTKeyWindow().rootViewController presentViewController:self.rootViewController + animated:YES + completion:nil]; } - - [self makeKeyAndVisible]; - [self becomeFirstResponder]; } } - (void)dismiss { - self.hidden = YES; - [self resignFirstResponder]; - [RCTSharedApplication().delegate.window makeKeyWindow]; + [self.rootViewController dismissViewControllerAnimated:YES completion:nil]; } - (void)reload