2525
2626#if RCT_DEV_MENU
2727
28- @class RCTRedBoxWindow ;
28+ @class RCTRedBoxController ;
2929
3030@interface UIButton (RCTRedBox)
3131
@@ -62,120 +62,171 @@ - (void)rct_addBlock:(RCTRedBoxButtonPressHandler)handler forControlEvents:(UICo
6262
6363@end
6464
65- @protocol RCTRedBoxWindowActionDelegate <NSObject >
65+ @protocol RCTRedBoxControllerActionDelegate <NSObject >
6666
67- - (void )redBoxWindow : (RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor : (RCTJSStackFrame *)stackFrame ;
68- - (void )reloadFromRedBoxWindow : (RCTRedBoxWindow *)redBoxWindow ;
67+ - (void )redBoxController : (RCTRedBoxController *)redBoxController openStackFrameInEditor : (RCTJSStackFrame *)stackFrame ;
68+ - (void )reloadFromRedBoxController : (RCTRedBoxController *)redBoxController ;
6969- (void )loadExtraDataViewController ;
7070
7171@end
7272
73- @interface RCTRedBoxWindow : NSObject <UITableViewDelegate, UITableViewDataSource>
74- @property (nonatomic , strong ) UIViewController *rootViewController;
75- @property (nonatomic , weak ) id <RCTRedBoxWindowActionDelegate> actionDelegate;
73+ @interface RCTRedBoxController : UIViewController <UITableViewDelegate, UITableViewDataSource>
74+ @property (nonatomic , weak ) id <RCTRedBoxControllerActionDelegate> actionDelegate;
7675@end
7776
78- @implementation RCTRedBoxWindow {
77+ @implementation RCTRedBoxController {
7978 UITableView *_stackTraceTableView;
8079 NSString *_lastErrorMessage;
8180 NSArray <RCTJSStackFrame *> *_lastStackTrace;
81+ NSArray <NSString *> *_customButtonTitles;
82+ NSArray <RCTRedBoxButtonPressHandler> *_customButtonHandlers;
8283 int _lastErrorCookie;
8384}
8485
85- - (instancetype )initWithFrame : (CGRect)frame
86- customButtonTitles : (NSArray <NSString *> *)customButtonTitles
87- customButtonHandlers : (NSArray <RCTRedBoxButtonPressHandler> *)customButtonHandlers
86+ - (instancetype )initWithCustomButtonTitles : (NSArray <NSString *> *)customButtonTitles
87+ customButtonHandlers : (NSArray <RCTRedBoxButtonPressHandler> *)customButtonHandlers
8888{
8989 if (self = [super init ]) {
9090 _lastErrorCookie = -1 ;
91+ _customButtonTitles = customButtonTitles;
92+ _customButtonHandlers = customButtonHandlers;
93+ }
94+
95+ return self;
96+ }
9197
92- _rootViewController = [UIViewController new ];
93- UIView *rootView = _rootViewController.view ;
94- rootView.frame = frame;
95- rootView.backgroundColor = [UIColor blackColor ];
98+ - (void )viewDidLoad
99+ {
100+ self.view .backgroundColor = [UIColor blackColor ];
96101
97- const CGFloat buttonHeight = 60 ;
102+ const CGFloat buttonHeight = 60 ;
98103
99- CGRect detailsFrame = rootView .bounds ;
100- detailsFrame.size .height -= buttonHeight + (double )[self bottomSafeViewHeight ];
104+ CGRect detailsFrame = self. view .bounds ;
105+ detailsFrame.size .height -= buttonHeight + (double )[self bottomSafeViewHeight ];
101106
102- _stackTraceTableView = [[UITableView alloc ] initWithFrame: detailsFrame style: UITableViewStylePlain];
103- _stackTraceTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
104- _stackTraceTableView.delegate = self;
105- _stackTraceTableView.dataSource = self;
106- _stackTraceTableView.backgroundColor = [UIColor clearColor ];
107- _stackTraceTableView.separatorColor = [UIColor colorWithWhite: 1 alpha: 0.3 ];
108- _stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
109- _stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
110- [rootView addSubview: _stackTraceTableView];
107+ _stackTraceTableView = [[UITableView alloc ] initWithFrame: detailsFrame style: UITableViewStylePlain];
108+ _stackTraceTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
109+ _stackTraceTableView.delegate = self;
110+ _stackTraceTableView.dataSource = self;
111+ _stackTraceTableView.backgroundColor = [UIColor clearColor ];
112+ _stackTraceTableView.separatorColor = [UIColor colorWithWhite: 1 alpha: 0.3 ];
113+ _stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
114+ _stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
115+ [ self .view addSubview: _stackTraceTableView];
111116
112117#if TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST
113- NSString *reloadText = @" Reload\n (\u2318 R)" ;
114- NSString *dismissText = @" Dismiss\n (ESC)" ;
115- NSString *copyText = @" Copy\n (\u2325\u2318 C)" ;
116- NSString *extraText = @" Extra Info\n (\u2318 E)" ;
118+ NSString *reloadText = @" Reload\n (\u2318 R)" ;
119+ NSString *dismissText = @" Dismiss\n (ESC)" ;
120+ NSString *copyText = @" Copy\n (\u2325\u2318 C)" ;
121+ NSString *extraText = @" Extra Info\n (\u2318 E)" ;
117122#else
118- NSString *reloadText = @" Reload JS" ;
119- NSString *dismissText = @" Dismiss" ;
120- NSString *copyText = @" Copy" ;
121- NSString *extraText = @" Extra Info" ;
123+ NSString *reloadText = @" Reload JS" ;
124+ NSString *dismissText = @" Dismiss" ;
125+ NSString *copyText = @" Copy" ;
126+ NSString *extraText = @" Extra Info" ;
122127#endif
123128
124- UIButton *dismissButton = [self redBoxButton: dismissText
125- accessibilityIdentifier: @" redbox-dismiss"
126- selector: @selector (dismiss )
127- block: nil ];
128- UIButton *reloadButton = [self redBoxButton: reloadText
129- accessibilityIdentifier: @" redbox-reload"
130- selector: @selector (reload )
131- block: nil ];
132- UIButton *copyButton = [self redBoxButton: copyText
133- accessibilityIdentifier: @" redbox-copy"
134- selector: @selector (copyStack )
135- block: nil ];
136- UIButton *extraButton = [self redBoxButton: extraText
137- accessibilityIdentifier: @" redbox-extra"
138- selector: @selector (showExtraDataViewController )
129+ UIButton *dismissButton = [self redBoxButton: dismissText
130+ accessibilityIdentifier: @" redbox-dismiss"
131+ selector: @selector (dismiss )
139132 block: nil ];
140-
141- CGFloat buttonWidth = frame.size .width / (CGFloat)(4 + [customButtonTitles count ]);
142- CGFloat bottomButtonHeight = frame.size .height - buttonHeight - (CGFloat)[self bottomSafeViewHeight ];
143- dismissButton.frame = CGRectMake (0 , bottomButtonHeight, buttonWidth, buttonHeight);
144- reloadButton.frame = CGRectMake (buttonWidth, bottomButtonHeight, buttonWidth, buttonHeight);
145- copyButton.frame = CGRectMake (buttonWidth * 2 , bottomButtonHeight, buttonWidth, buttonHeight);
146- extraButton.frame = CGRectMake (buttonWidth * 3 , bottomButtonHeight, buttonWidth, buttonHeight);
147-
148- [rootView addSubview: dismissButton];
149- [rootView addSubview: reloadButton];
150- [rootView addSubview: copyButton];
151- [rootView addSubview: extraButton];
152-
153- for (NSUInteger i = 0 ; i < [customButtonTitles count ]; i++) {
154- UIButton *button = [self redBoxButton: customButtonTitles[i]
155- accessibilityIdentifier: @" "
156- selector: nil
157- block: customButtonHandlers[i]];
158- button.frame = CGRectMake (buttonWidth * (double )(4 + i), bottomButtonHeight, buttonWidth, buttonHeight);
159- [rootView addSubview: button];
160- }
161-
162- UIView *topBorder =
163- [[UIView alloc ] initWithFrame: CGRectMake (0 , bottomButtonHeight + 1 , rootView.frame.size.width, 1 )];
164- topBorder.backgroundColor = [UIColor colorWithRed: 0.70 green: 0.70 blue: 0.70 alpha: 1.0 ];
165-
166- [rootView addSubview: topBorder];
167-
168- UIView *bottomSafeView = [UIView new ];
169- bottomSafeView.backgroundColor = [UIColor colorWithRed: 0.1 green: 0.1 blue: 0.1 alpha: 1 ];
170- bottomSafeView.frame = CGRectMake (
171- 0 ,
172- frame.size .height - (CGFloat)[self bottomSafeViewHeight ],
173- frame.size .width ,
174- (CGFloat)[self bottomSafeViewHeight ]);
175-
176- [rootView addSubview: bottomSafeView];
133+ UIButton *reloadButton = [self redBoxButton: reloadText
134+ accessibilityIdentifier: @" redbox-reload"
135+ selector: @selector (reload )
136+ block: nil ];
137+ UIButton *copyButton = [self redBoxButton: copyText
138+ accessibilityIdentifier: @" redbox-copy"
139+ selector: @selector (copyStack )
140+ block: nil ];
141+ UIButton *extraButton = [self redBoxButton: extraText
142+ accessibilityIdentifier: @" redbox-extra"
143+ selector: @selector (showExtraDataViewController )
144+ block: nil ];
145+
146+ [dismissButton.heightAnchor constraintEqualToConstant: buttonHeight].active = YES ;
147+ [reloadButton.heightAnchor constraintEqualToConstant: buttonHeight].active = YES ;
148+ [copyButton.heightAnchor constraintEqualToConstant: buttonHeight].active = YES ;
149+ [extraButton.heightAnchor constraintEqualToConstant: buttonHeight].active = YES ;
150+
151+ UIStackView *buttonStackView = [[UIStackView alloc ] init ];
152+ buttonStackView.translatesAutoresizingMaskIntoConstraints = NO ;
153+ buttonStackView.axis = UILayoutConstraintAxisHorizontal;
154+ buttonStackView.distribution = UIStackViewDistributionFillEqually;
155+ buttonStackView.alignment = UIStackViewAlignmentTop;
156+
157+ [buttonStackView.heightAnchor constraintEqualToConstant: buttonHeight + [self bottomSafeViewHeight ]].active = YES ;
158+ buttonStackView.backgroundColor = [UIColor colorWithRed: 0.1 green: 0.1 blue: 0.1 alpha: 1 ];
159+
160+ [buttonStackView addArrangedSubview: dismissButton];
161+ [buttonStackView addArrangedSubview: reloadButton];
162+ [buttonStackView addArrangedSubview: copyButton];
163+ [buttonStackView addArrangedSubview: extraButton];
164+
165+ [self .view addSubview: buttonStackView];
166+
167+ [self .view addConstraint: [NSLayoutConstraint constraintWithItem: buttonStackView
168+ attribute: NSLayoutAttributeLeading
169+ relatedBy: NSLayoutRelationEqual
170+ toItem: self .view
171+ attribute: NSLayoutAttributeLeading
172+ multiplier: 1.0
173+ constant: 0 ]];
174+
175+ [self .view addConstraint: [NSLayoutConstraint constraintWithItem: buttonStackView
176+ attribute: NSLayoutAttributeTrailing
177+ relatedBy: NSLayoutRelationEqual
178+ toItem: self .view
179+ attribute: NSLayoutAttributeTrailing
180+ multiplier: 1.0
181+ constant: 0 ]];
182+
183+ [self .view addConstraint: [NSLayoutConstraint constraintWithItem: buttonStackView
184+ attribute: NSLayoutAttributeBottom
185+ relatedBy: NSLayoutRelationEqual
186+ toItem: self .view
187+ attribute: NSLayoutAttributeBottom
188+ multiplier: 1.0
189+ constant: 0 ]];
190+
191+ for (NSUInteger i = 0 ; i < [_customButtonTitles count ]; i++) {
192+ UIButton *button = [self redBoxButton: _customButtonTitles[i]
193+ accessibilityIdentifier: @" "
194+ selector: nil
195+ block: _customButtonHandlers[i]];
196+ [button.heightAnchor constraintEqualToConstant: buttonHeight].active = YES ;
197+ [buttonStackView addArrangedSubview: button];
177198 }
178- return self;
199+
200+ UIView *topBorder = [[UIView alloc ] init ];
201+ topBorder.translatesAutoresizingMaskIntoConstraints = NO ;
202+ topBorder.backgroundColor = [UIColor colorWithRed: 0.70 green: 0.70 blue: 0.70 alpha: 1.0 ];
203+ [topBorder.heightAnchor constraintEqualToConstant: 1 ].active = YES ;
204+
205+ [self .view addSubview: topBorder];
206+
207+ [self .view addConstraint: [NSLayoutConstraint constraintWithItem: topBorder
208+ attribute: NSLayoutAttributeLeading
209+ relatedBy: NSLayoutRelationEqual
210+ toItem: self .view
211+ attribute: NSLayoutAttributeLeading
212+ multiplier: 1.0
213+ constant: 0 ]];
214+
215+ [self .view addConstraint: [NSLayoutConstraint constraintWithItem: topBorder
216+ attribute: NSLayoutAttributeTrailing
217+ relatedBy: NSLayoutRelationEqual
218+ toItem: self .view
219+ attribute: NSLayoutAttributeTrailing
220+ multiplier: 1.0
221+ constant: 0 ]];
222+
223+ [self .view addConstraint: [NSLayoutConstraint constraintWithItem: topBorder
224+ attribute: NSLayoutAttributeBottom
225+ relatedBy: NSLayoutRelationEqual
226+ toItem: buttonStackView
227+ attribute: NSLayoutAttributeTop
228+ multiplier: 1.0
229+ constant: 0 ]];
179230}
180231
181232- (UIButton *)redBoxButton : (NSString *)title
@@ -226,7 +277,7 @@ - (void)showErrorMessage:(NSString *)message
226277 // Remove ANSI color codes from the message
227278 NSString *messageWithoutAnsi = [self stripAnsi: message];
228279
229- BOOL isRootViewControllerPresented = self.rootViewController . presentingViewController != nil ;
280+ BOOL isRootViewControllerPresented = self.presentingViewController != nil ;
230281 // Show if this is a new message, or if we're updating the previous message
231282 BOOL isNew = !isRootViewControllerPresented && !isUpdate;
232283 BOOL isUpdateForSameMessage = !isNew &&
@@ -246,19 +297,19 @@ - (void)showErrorMessage:(NSString *)message
246297 [_stackTraceTableView scrollToRowAtIndexPath: [NSIndexPath indexPathForRow: 0 inSection: 0 ]
247298 atScrollPosition: UITableViewScrollPositionTop
248299 animated: NO ];
249- [RCTKeyWindow ().rootViewController presentViewController: self .rootViewController animated: YES completion: nil ];
300+ [RCTKeyWindow ().rootViewController presentViewController: self animated: YES completion: nil ];
250301 }
251302 }
252303}
253304
254305- (void )dismiss
255306{
256- [self .rootViewController dismissViewControllerAnimated: YES completion: nil ];
307+ [self dismissViewControllerAnimated: YES completion: nil ];
257308}
258309
259310- (void )reload
260311{
261- [_actionDelegate reloadFromRedBoxWindow :self ];
312+ [_actionDelegate reloadFromRedBoxController :self ];
262313}
263314
264315- (void )showExtraDataViewController
@@ -396,7 +447,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
396447 if (indexPath.section == 1 ) {
397448 NSUInteger row = indexPath.row ;
398449 RCTJSStackFrame *stackFrame = _lastStackTrace[row];
399- [_actionDelegate redBoxWindow :self openStackFrameInEditor: stackFrame];
450+ [_actionDelegate redBoxController :self openStackFrameInEditor: stackFrame];
400451 }
401452 [tableView deselectRowAtIndexPath: indexPath animated: YES ];
402453}
@@ -438,13 +489,13 @@ - (BOOL)canBecomeFirstResponder
438489
439490@interface RCTRedBox () <
440491 RCTInvalidating,
441- RCTRedBoxWindowActionDelegate ,
492+ RCTRedBoxControllerActionDelegate ,
442493 RCTRedBoxExtraDataActionDelegate,
443494 NativeRedBoxSpec>
444495@end
445496
446497@implementation RCTRedBox {
447- RCTRedBoxWindow *_window ;
498+ RCTRedBoxController *_controller ;
448499 NSMutableArray <id <RCTErrorCustomizer>> *_errorCustomizers;
449500 RCTRedBoxExtraDataViewController *_extraDataViewController;
450501 NSMutableArray <NSString *> *_customButtonTitles;
@@ -592,31 +643,27 @@ - (void)showErrorMessage:(NSString *)message
592643 [[self ->_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" collectRedBoxExtraData"
593644 body: nil ];
594645#pragma clang diagnostic pop
595-
596- if (!self->_window ) {
597- self->_window = [[RCTRedBoxWindow alloc ] initWithFrame: [UIScreen mainScreen ].bounds
598- customButtonTitles: self ->_customButtonTitles
599- customButtonHandlers: self ->_customButtonHandlers];
600- self->_window .actionDelegate = self;
646+ if (!self->_controller ) {
647+ self->_controller = [[RCTRedBoxController alloc ] initWithCustomButtonTitles: self ->_customButtonTitles
648+ customButtonHandlers: self ->_customButtonHandlers];
649+ self->_controller .actionDelegate = self;
601650 }
602651
603652 RCTErrorInfo *errorInfo = [[RCTErrorInfo alloc ] initWithErrorMessage: message stack: stack];
604653 errorInfo = [self _customizeError: errorInfo];
605- [self ->_window showErrorMessage: errorInfo.errorMessage
606- withStack: errorInfo.stack
607- isUpdate: isUpdate
608- errorCookie: errorCookie];
654+ [self ->_controller showErrorMessage: errorInfo.errorMessage
655+ withStack: errorInfo.stack
656+ isUpdate: isUpdate
657+ errorCookie: errorCookie];
609658 });
610659}
611660
612661- (void )loadExtraDataViewController
613662{
614663 dispatch_async (dispatch_get_main_queue (), ^{
615664 // Make sure the CMD+E shortcut doesn't call this twice
616- if (self->_extraDataViewController != nil && ![self ->_window.rootViewController presentedViewController ]) {
617- [self ->_window.rootViewController presentViewController: self ->_extraDataViewController
618- animated: YES
619- completion: nil ];
665+ if (self->_extraDataViewController != nil && ![self ->_controller presentedViewController ]) {
666+ [self ->_controller presentViewController: self ->_extraDataViewController animated: YES completion: nil ];
620667 }
621668 });
622669}
@@ -629,7 +676,7 @@ - (void)loadExtraDataViewController
629676RCT_EXPORT_METHOD (dismiss)
630677{
631678 dispatch_async (dispatch_get_main_queue (), ^{
632- [self ->_window dismiss ];
679+ [self ->_controller dismiss ];
633680 });
634681}
635682
@@ -638,7 +685,8 @@ - (void)invalidate
638685 [self dismiss ];
639686}
640687
641- - (void )redBoxWindow : (__unused RCTRedBoxWindow *)redBoxWindow openStackFrameInEditor : (RCTJSStackFrame *)stackFrame
688+ - (void )redBoxController : (__unused RCTRedBoxController *)redBoxController
689+ openStackFrameInEditor : (RCTJSStackFrame *)stackFrame
642690{
643691 NSURL *const bundleURL = _overrideBundleURL ?: _bundleManager.bundleURL ;
644692 if (![bundleURL.scheme hasPrefix: @" http" ]) {
@@ -661,10 +709,10 @@ - (void)redBoxWindow:(__unused RCTRedBoxWindow *)redBoxWindow openStackFrameInEd
661709- (void )reload
662710{
663711 // Window is not used and can be nil
664- [self reloadFromRedBoxWindow :nil ];
712+ [self reloadFromRedBoxController :nil ];
665713}
666714
667- - (void )reloadFromRedBoxWindow : (__unused RCTRedBoxWindow *)redBoxWindow
715+ - (void )reloadFromRedBoxController : (__unused RCTRedBoxController *)redBoxController
668716{
669717 if (_overrideReloadAction) {
670718 _overrideReloadAction ();
0 commit comments