Skip to content

Commit

Permalink
Refactor Redbox, Logbox, and DevLoadingView as Sheets (#2151)
Browse files Browse the repository at this point in the history
* fix(iOS): adjust RCTRedBox to work for iPad and support orientation changes (facebook#41217)

Summary:
When opening `RCTRedBox` on an iPad (and also visionOS) there was an issue with buttons width going out of screen. When changing screen orientation, RedBox wasn't recalculating view positions.

**Root cause**: Getting frame of root view to display this modal and basing all calculations on it.

**Solution**: Use Auto Layout to build UI that responds to orientation changes and device specific modal presentation.

I've also tested it with adding custom buttons to RedBox and it works properly.

[IOS] [FIXED] - adjust RCTRedBox to work for iPad and support orientation changes

Pull Request resolved: facebook#41217

Test Plan:
Launch the app without metro running and check out RedBox that's shown there. Also change screen orientation to see proper recalculation of view positions.

https://github.com/facebook/react-native/assets/52801365/892dcfe7-246f-4f36-be37-12c139c207ac

https://github.com/facebook/react-native/assets/52801365/dfd0c3d8-5997-462d-97ec-dcc3de452e26

Reviewed By: GijsWeterings

Differential Revision: D50734569

Pulled By: javache

fbshipit-source-id: 51b854a47caf90ae46fcd32c4adcc64ec2ceb63f

* refactor: use less verbose API for RCTRedBox constraints (facebook#42261)

Summary:
This PR is a continuation of my previous PR where I refactored RCTRedBox to use Auto Layout (facebook#41217). This PR uses less verbose API for defining constraints.

## Changelog:

[IOS] [CHANGED] - use less verbose Auto Layout API for RCTRedBox constraints

Pull Request resolved: facebook#42261

Test Plan:
Launch the app without metro enabled to see the RCTRedBox

![CleanShot 2024-01-12 at 14 54 20@2x](https://github.com/facebook/react-native/assets/52801365/32ee9916-3e32-46c3-9f6b-c313631aa1e5)
![CleanShot 2024-01-12 at 14 54 16@2x](https://github.com/facebook/react-native/assets/52801365/c625b9b9-b462-4e67-831f-0192427bbe93)

Reviewed By: NickGerleman

Differential Revision: D52730458

Pulled By: javache

fbshipit-source-id: dc7227e7b6e3238c195342cb0460850b57eb75c3

* refactor Redbox on macOS and present as a sheet

* present RCTDevLoadingView as a sheet

* refactor Logbox and present as a sheet

* change RCTRootView.loadingView to RCTPlatformView

* update Podfile.lock

* PR feedback

---------

Co-authored-by: Oskar Kwaśniewski <[email protected]>
  • Loading branch information
Saadnajmi and okwasniewski authored Aug 2, 2024
1 parent 042cd6c commit 67a4a0f
Show file tree
Hide file tree
Showing 12 changed files with 619 additions and 798 deletions.
2 changes: 1 addition & 1 deletion packages/react-native/React/Base/RCTRootView.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ extern
* with a blank screen. By default this is nil, but you can override it with
* (for example) a UIActivityIndicatorView or a placeholder image.
*/
@property (nonatomic, strong, nullable) RCTUIView *loadingView; // [macOS]
@property (nonatomic, strong, nullable) RCTPlatformView *loadingView; // [macOS]

/**
* When set, any touches on the RCTRootView that are not matched up to any of the child
Expand Down
21 changes: 8 additions & 13 deletions packages/react-native/React/Base/RCTRootView.m
Original file line number Diff line number Diff line change
Expand Up @@ -166,20 +166,15 @@ - (void)layoutSubviews
#if !TARGET_OS_OSX // [macOS]
_loadingView.center = (CGPoint){CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)};
#else // [macOS
NSRect bounds = self.bounds;
NSSize loadingViewSize = _loadingView.frame.size;
CGFloat scale = self.window.backingScaleFactor;
if (scale == 0.0 && RCTRunningInTestEnvironment()) {
// When running in the test environment the view is not on screen.
// Use a scaleFactor of 1 so that the test results are machine independent.
scale = 1;
}
RCTAssert(scale != 0.0, @"Layout occurs before the view is in a window?");

_loadingView.frameOrigin = NSMakePoint(
RCTRoundPixelValue(bounds.origin.x + ((bounds.size.width - loadingViewSize.width) / 2), scale),
RCTRoundPixelValue(bounds.origin.y + ((bounds.size.height - loadingViewSize.height) / 2), scale)
CGFloat centerX = CGRectGetMidX(self.bounds);
CGFloat centerY = CGRectGetMidY(self.bounds);
NSRect newFrame = NSMakeRect(
centerX - _loadingView.frame.size.width / 2.0,
centerY - _loadingView.frame.size.height / 2.0,
_loadingView.frame.size.width,
_loadingView.frame.size.height
);
_loadingView.frame = newFrame;
#endif // macOS]
}

Expand Down
2 changes: 2 additions & 0 deletions packages/react-native/React/Base/RCTUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,11 @@ NS_ASSUME_NONNULL_END
#if !TARGET_OS_OSX
typedef UIApplication RCTUIApplication;
typedef UIWindow RCTUIWindow;
typedef UIViewController RCTUIViewController;
#else
typedef NSApplication RCTUIApplication;
typedef NSWindow RCTUIWindow;
typedef NSViewController RCTUIViewController;
#endif

//
Expand Down
1 change: 1 addition & 0 deletions packages/react-native/React/Base/macOS/RCTUIKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ - (void)drawRect:(CGRect)rect

- (void)layout
{
[super layout];
if (self.window != nil) {
[self layoutSubviews];
}
Expand Down
19 changes: 6 additions & 13 deletions packages/react-native/React/CoreModules/RCTDevLoadingView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo

if (!self->_window && !RCTRunningInTestEnvironment()) {
#if !TARGET_OS_OSX // [macOS]
UIWindow *window = RCTKeyWindow();
UIWindow *window = RCTKeyWindow(); // [macOS]
CGFloat windowWidth = window.bounds.size.width;

self->_window = [[UIWindow alloc] initWithWindowScene:window.windowScene];
Expand All @@ -138,8 +138,7 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo
self->_label.font = [UIFont monospacedDigitSystemFontOfSize:12.0 weight:UIFontWeightRegular];
self->_label.textAlignment = NSTextAlignmentCenter;
#else // [macOS
NSRect screenFrame = [NSScreen mainScreen].visibleFrame;
self->_window = [[NSPanel alloc] initWithContentRect:NSMakeRect(screenFrame.origin.x + round((screenFrame.size.width - 375) / 2), screenFrame.size.height - 20, 375, 19)
self->_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 375, 20)
styleMask:NSWindowStyleMaskBorderless
backing:NSBackingStoreBuffered
defer:YES];
Expand All @@ -153,6 +152,7 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo
label.selectable = NO;
label.wantsLayer = YES;
label.layer.cornerRadius = label.frame.size.height / 3;
label.layer.cornerCurve = kCACornerCurveContinuous;
self->_label = label;
[[self->_window contentView] addSubview:label];
#endif // macOS]
Expand All @@ -169,7 +169,7 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo
self->_label.textColor = color;

self->_label.backgroundColor = backgroundColor;
[self->_window orderFront:nil];
[RCTKeyWindow() beginSheet:self->_window completionHandler:nil];
#endif // macOS]
});

Expand All @@ -195,10 +195,10 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo

dispatch_async(dispatch_get_main_queue(), ^{
self->_hiding = YES;
#if !TARGET_OS_OSX // [macOS]
const NSTimeInterval MIN_PRESENTED_TIME = 0.6;
NSTimeInterval presentedTime = [[NSDate date] timeIntervalSinceDate:self->_showDate];
NSTimeInterval delay = MAX(0, MIN_PRESENTED_TIME - presentedTime);
#if !TARGET_OS_OSX // [macOS]
CGRect windowFrame = self->_window.frame;
[UIView animateWithDuration:0.25
delay:delay
Expand All @@ -213,14 +213,7 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo
self->_hiding = false;
}];
#else // [macOS]
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[NSAnimationContext runAnimationGroup:^(__unused NSAnimationContext *context) {
self->_window.animator.alphaValue = 0.0;
} completionHandler:^{
[self->_window close];
self->_window = nil;
}];
});
[RCTKeyWindow() endSheet:self->_window];
#endif // macOS]
});
}
Expand Down
12 changes: 0 additions & 12 deletions packages/react-native/React/CoreModules/RCTLogBox.mm
Original file line number Diff line number Diff line change
Expand Up @@ -55,27 +55,15 @@ - (void)setSurfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter
}

if (strongSelf->_bridgelessSurfacePresenter) {
#if !TARGET_OS_OSX // [macOS]
strongSelf->_view = [[RCTLogBoxView alloc] initWithWindow:RCTKeyWindow()
surfacePresenter:strongSelf->_bridgelessSurfacePresenter];
#else // [macOS
strongSelf->_view = [[RCTLogBoxView alloc] initWithSurfacePresenter:strongSelf->_bridgelessSurfacePresenter];
#endif // macOS]
[strongSelf->_view show];
} else if (strongSelf->_bridge && strongSelf->_bridge.valid) {
if (strongSelf->_bridge.surfacePresenter) {
#if !TARGET_OS_OSX // [macOS]
strongSelf->_view = [[RCTLogBoxView alloc] initWithWindow:RCTKeyWindow()
surfacePresenter:strongSelf->_bridge.surfacePresenter];
#else // [macOS
strongSelf->_view = [[RCTLogBoxView alloc] initWithSurfacePresenter:strongSelf->_bridge.surfacePresenter];
#endif // macOS]
} else {
#if !TARGET_OS_OSX // [macOS]
strongSelf->_view = [[RCTLogBoxView alloc] initWithWindow:RCTKeyWindow() bridge:strongSelf->_bridge];
#else // [macOS
strongSelf->_view = [[RCTLogBoxView alloc] initWithBridge:self->_bridge];
#endif // macOS]
}
[strongSelf->_view show];
}
Expand Down
28 changes: 11 additions & 17 deletions packages/react-native/React/CoreModules/RCTLogBoxView.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,25 @@
#import <React/RCTUIKit.h> // [macOS]

#if !TARGET_OS_OSX // [macOS]

@interface RCTLogBoxView : UIWindow
#else // [macOS
@interface RCTLogBoxView : NSWindow
#endif // macOS]

#if !TARGET_OS_OSX // [macOS]
- (instancetype)initWithFrame:(CGRect)frame;
#endif // [macOS]

- (void)createRootViewController:(UIView *)view;
- (void)createRootViewController:(RCTUIView *)view; // [macOS]

- (instancetype)initWithWindow:(UIWindow *)window bridge:(RCTBridge *)bridge;
- (instancetype)initWithWindow:(UIWindow *)window surfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter;
- (instancetype)initWithWindow:(RCTUIWindow *)window bridge:(RCTBridge *)bridge; // [macOS]
- (instancetype)initWithWindow:(RCTUIWindow *)window surfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter; // [macOS]

- (void)show;

@end

#else // [macOS

@interface RCTLogBoxView : NSWindow

- (instancetype)initWithSurfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter;
- (instancetype)initWithBridge:(RCTBridge *)bridge;

#if TARGET_OS_OSX // [macOS
- (void)setHidden:(BOOL)hidden;

- (void)show;
#endif // macOS]

@end

#endif // macOS]

Loading

0 comments on commit 67a4a0f

Please sign in to comment.