mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
fabric: Migrate ModalHostView
Summary: Implements `ModalHostView` in Fabric. Reviewed By: shergin Differential Revision: D16090949 fbshipit-source-id: fe2c87063863e7f3c6c8dcc934a2cdb0b1aedaac
This commit is contained in:
committed by
Facebook Github Bot
parent
db17e969fa
commit
faf9130560
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTViewComponentView.h>
|
||||
|
||||
/**
|
||||
* UIView class for root <ModalHostView> component.
|
||||
*/
|
||||
@interface RNModalHostViewComponentView : RCTViewComponentView
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNModalHostViewComponentView.h"
|
||||
#import "RNModalHostViewController.h"
|
||||
|
||||
#import <React/UIView+React.h>
|
||||
#import <react/components/modal/ModalHostViewComponentDescriptor.h>
|
||||
#import <react/components/modal/ModalHostViewState.h>
|
||||
#import <react/components/rncore/EventEmitters.h>
|
||||
#import <react/components/rncore/Props.h>
|
||||
#import "RCTConversions.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
static UIInterfaceOrientationMask supportedOrientationsMask(ModalHostViewSupportedOrientationsMask mask)
|
||||
{
|
||||
UIInterfaceOrientationMask supportedOrientations = 0;
|
||||
|
||||
if (mask & ModalHostViewSupportedOrientations::Portrait) {
|
||||
supportedOrientations |= UIInterfaceOrientationMaskPortrait;
|
||||
}
|
||||
|
||||
if (mask & ModalHostViewSupportedOrientations::PortraitUpsideDown) {
|
||||
supportedOrientations |= UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
}
|
||||
|
||||
if (mask & ModalHostViewSupportedOrientations::Landscape) {
|
||||
supportedOrientations |= UIInterfaceOrientationMaskLandscape;
|
||||
}
|
||||
|
||||
if (mask & ModalHostViewSupportedOrientations::LandscapeLeft) {
|
||||
supportedOrientations |= UIInterfaceOrientationMaskLandscapeLeft;
|
||||
}
|
||||
|
||||
if (mask & ModalHostViewSupportedOrientations::LandscapeRight) {
|
||||
supportedOrientations |= UIInterfaceOrientationMaskLandscapeRight;
|
||||
}
|
||||
|
||||
if (supportedOrientations == 0) {
|
||||
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
} else {
|
||||
return UIInterfaceOrientationMaskPortrait;
|
||||
}
|
||||
}
|
||||
|
||||
return supportedOrientations;
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::tuple<BOOL, UIModalTransitionStyle> animationConfiguration(ModalHostViewAnimationType const animation)
|
||||
{
|
||||
switch (animation) {
|
||||
case ModalHostViewAnimationType::None:
|
||||
return std::make_tuple(NO, UIModalTransitionStyleCoverVertical);
|
||||
case ModalHostViewAnimationType::Slide:
|
||||
return std::make_tuple(YES, UIModalTransitionStyleCoverVertical);
|
||||
case ModalHostViewAnimationType::Fade:
|
||||
return std::make_tuple(YES, UIModalTransitionStyleCrossDissolve);
|
||||
}
|
||||
}
|
||||
|
||||
static UIModalPresentationStyle presentationConfiguration(ModalHostViewProps const &props)
|
||||
{
|
||||
if (props.transparent) {
|
||||
return UIModalPresentationOverFullScreen;
|
||||
}
|
||||
switch (props.presentationStyle) {
|
||||
case ModalHostViewPresentationStyle::FullScreen:
|
||||
return UIModalPresentationFullScreen;
|
||||
case ModalHostViewPresentationStyle::PageSheet:
|
||||
return UIModalPresentationPageSheet;
|
||||
case ModalHostViewPresentationStyle::FormSheet:
|
||||
return UIModalPresentationFormSheet;
|
||||
case ModalHostViewPresentationStyle::OverFullScreen:
|
||||
return UIModalPresentationOverFullScreen;
|
||||
}
|
||||
}
|
||||
|
||||
static ModalHostViewOnOrientationChangeStruct onOrientationChangeStruct(CGRect rect)
|
||||
{
|
||||
auto orientation = rect.size.width < rect.size.height ? ModalHostViewOnOrientationChangeOrientationStruct::Portrait
|
||||
: ModalHostViewOnOrientationChangeOrientationStruct::Landscape;
|
||||
return {orientation};
|
||||
}
|
||||
|
||||
@interface RNModalHostViewComponentView () <RNModalHostViewControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNModalHostViewComponentView {
|
||||
RNModalHostViewController *_viewController;
|
||||
ModalHostViewShadowNode::ConcreteState::Shared _state;
|
||||
BOOL _shouldAnimatePresentation;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
static const auto defaultProps = std::make_shared<const ModalHostViewProps>();
|
||||
_props = defaultProps;
|
||||
_shouldAnimatePresentation = YES;
|
||||
_viewController = [RNModalHostViewController new];
|
||||
_viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
|
||||
_viewController.delegate = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isViewControllerPresented
|
||||
{
|
||||
return _viewController.presentingViewController != nil;
|
||||
}
|
||||
|
||||
- (void)ensurePresentedOnlyIfNeeded
|
||||
{
|
||||
BOOL shouldBePresented = !self.isViewControllerPresented && self.window;
|
||||
if (shouldBePresented) {
|
||||
UIViewController *controller = [self reactViewController];
|
||||
return [controller
|
||||
presentViewController:_viewController
|
||||
animated:_shouldAnimatePresentation
|
||||
completion:^{
|
||||
ModalHostViewOnShowStruct onShow;
|
||||
std::dynamic_pointer_cast<const ModalHostViewEventEmitter>(self->_eventEmitter)->onShow(onShow);
|
||||
}];
|
||||
}
|
||||
|
||||
BOOL shouldBeHidden = self.isViewControllerPresented && !self.superview;
|
||||
if (shouldBeHidden) {
|
||||
[_viewController dismissViewControllerAnimated:_shouldAnimatePresentation completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
[super didMoveToWindow];
|
||||
[self ensurePresentedOnlyIfNeeded];
|
||||
}
|
||||
|
||||
- (void)didMoveToSuperview
|
||||
{
|
||||
[super didMoveToSuperview];
|
||||
[self ensurePresentedOnlyIfNeeded];
|
||||
}
|
||||
|
||||
#pragma mark - RNModalHostViewControllerDelegate
|
||||
|
||||
- (void)boundsDidChange:(CGRect)newBounds
|
||||
{
|
||||
std::dynamic_pointer_cast<const ModalHostViewEventEmitter>(_eventEmitter)
|
||||
->onOrientationChange(onOrientationChangeStruct(newBounds));
|
||||
|
||||
if (_state != nullptr) {
|
||||
auto newState = ModalHostViewState{RCTSizeFromCGSize(newBounds.size)};
|
||||
_state->updateState(std::move(newState));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
{
|
||||
return concreteComponentDescriptorProvider<ModalHostViewComponentDescriptor>();
|
||||
}
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const ModalHostViewProps>(props);
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
_viewController.supportedInterfaceOrientations = supportedOrientationsMask(newProps.supportedOrientations);
|
||||
#endif
|
||||
|
||||
std::tuple<BOOL, UIModalTransitionStyle> result = animationConfiguration(newProps.animationType);
|
||||
_shouldAnimatePresentation = std::get<0>(result);
|
||||
_viewController.modalTransitionStyle = std::get<1>(result);
|
||||
|
||||
_viewController.modalPresentationStyle = presentationConfiguration(newProps);
|
||||
|
||||
[super updateProps:props oldProps:oldProps];
|
||||
}
|
||||
|
||||
- (void)updateState:(facebook::react::State::Shared const &)state
|
||||
oldState:(facebook::react::State::Shared const &)oldState
|
||||
{
|
||||
_state = std::static_pointer_cast<const ModalHostViewShadowNode::ConcreteState>(state);
|
||||
}
|
||||
|
||||
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
||||
{
|
||||
[_viewController.view insertSubview:childComponentView atIndex:index];
|
||||
}
|
||||
|
||||
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
||||
{
|
||||
[childComponentView removeFromSuperview];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol RNModalHostViewControllerDelegate <NSObject>
|
||||
- (void)boundsDidChange:(CGRect)newBounds;
|
||||
@end
|
||||
|
||||
@interface RNModalHostViewController : UIViewController
|
||||
|
||||
@property (nonatomic, weak) id<RNModalHostViewControllerDelegate> delegate;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations;
|
||||
#endif
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNModalHostViewController.h"
|
||||
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTSurfaceTouchHandler.h>
|
||||
|
||||
@implementation RNModalHostViewController {
|
||||
CGRect _lastViewBounds;
|
||||
RCTSurfaceTouchHandler *_touchHandler;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
_touchHandler = [RCTSurfaceTouchHandler new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
if (!CGRectEqualToRect(_lastViewBounds, self.view.bounds)) {
|
||||
[_delegate boundsDidChange:self.view.bounds];
|
||||
_lastViewBounds = self.view.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
[_touchHandler attachToView:self.view];
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return [RCTSharedApplication() statusBarStyle];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
_lastViewBounds = CGRectZero;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return [RCTSharedApplication() isStatusBarHidden];
|
||||
}
|
||||
|
||||
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)())completion
|
||||
{
|
||||
UIView *snapshot = [self.view snapshotViewAfterScreenUpdates:NO];
|
||||
[self.view addSubview:snapshot];
|
||||
|
||||
[super dismissViewControllerAnimated:flag
|
||||
completion:^{
|
||||
[snapshot removeFromSuperview];
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#if RCT_DEV
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
||||
{
|
||||
UIInterfaceOrientationMask appSupportedOrientationsMask =
|
||||
[RCTSharedApplication() supportedInterfaceOrientationsForWindow:[RCTSharedApplication() keyWindow]];
|
||||
if (!(_supportedInterfaceOrientations & appSupportedOrientationsMask)) {
|
||||
RCTLogError(
|
||||
@"Modal was presented with 0x%x orientations mask but the application only supports 0x%x."
|
||||
@"Add more interface orientations to your app's Info.plist to fix this."
|
||||
@"NOTE: This will crash in non-dev mode.",
|
||||
(unsigned)_supportedInterfaceOrientations,
|
||||
(unsigned)appSupportedOrientationsMask);
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
return _supportedInterfaceOrientations;
|
||||
}
|
||||
#endif // RCT_DEV
|
||||
#endif // !TARGET_OS_TV
|
||||
|
||||
@end
|
||||
@@ -23,6 +23,7 @@
|
||||
#import "RCTSwitchComponentView.h"
|
||||
#import "RCTUnimplementedNativeComponentView.h"
|
||||
#import "RCTViewComponentView.h"
|
||||
#import "RNModalHostViewComponentView.h"
|
||||
#import "RNPullToRefreshViewComponentView.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
@@ -47,6 +48,7 @@ using namespace facebook::react;
|
||||
[componentViewFactory registerComponentViewClass:[RCTSliderComponentView class]];
|
||||
[componentViewFactory registerComponentViewClass:[RCTSwitchComponentView class]];
|
||||
[componentViewFactory registerComponentViewClass:[RCTUnimplementedNativeComponentView class]];
|
||||
[componentViewFactory registerComponentViewClass:[RNModalHostViewComponentView class]];
|
||||
|
||||
return componentViewFactory;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@ namespace react {
|
||||
class ModalHostViewComponentDescriptor final
|
||||
: public ConcreteComponentDescriptor<ModalHostViewShadowNode> {
|
||||
public:
|
||||
#ifdef ANDROID
|
||||
ModalHostViewComponentDescriptor(EventDispatcher::Shared eventDispatcher)
|
||||
: ConcreteComponentDescriptor(eventDispatcher) {}
|
||||
#else
|
||||
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
||||
#endif
|
||||
|
||||
void adopt(UnsharedShadowNode shadowNode) const override {
|
||||
assert(std::dynamic_pointer_cast<ModalHostViewShadowNode>(shadowNode));
|
||||
|
||||
Reference in New Issue
Block a user