You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
6.1 KiB
232 lines
6.1 KiB
#import <UIKit/UIKit.h>
|
|
|
|
#import "RNSFullWindowOverlay.h"
|
|
|
|
#ifdef RN_FABRIC_ENABLED
|
|
#import <React/RCTConversions.h>
|
|
#import <React/RCTFabricComponentsPlugins.h>
|
|
#import <React/RCTSurfaceTouchHandler.h>
|
|
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
|
|
#import <react/renderer/components/rnscreens/Props.h>
|
|
#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
|
|
#else
|
|
#import <React/RCTTouchHandler.h>
|
|
#endif // RN_FABRIC_ENABLED
|
|
|
|
@implementation RNSFullWindowOverlayContainer
|
|
|
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
|
{
|
|
for (UIView *view in [self subviews]) {
|
|
if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
|
{
|
|
BOOL canReceiveTouchEvents = ([self isUserInteractionEnabled] && ![self isHidden]);
|
|
if (!canReceiveTouchEvents) {
|
|
return nil;
|
|
}
|
|
|
|
// `hitSubview` is the topmost subview which was hit. The hit point can
|
|
// be outside the bounds of `view` (e.g., if -clipsToBounds is NO).
|
|
UIView *hitSubview = nil;
|
|
BOOL isPointInside = [self pointInside:point withEvent:event];
|
|
if (![self clipsToBounds] || isPointInside) {
|
|
// Take z-index into account when calculating the touch target.
|
|
NSArray<UIView *> *sortedSubviews = [self reactZIndexSortedSubviews];
|
|
|
|
// The default behaviour of UIKit is that if a view does not contain a point,
|
|
// then no subviews will be returned from hit testing, even if they contain
|
|
// the hit point. By doing hit testing directly on the subviews, we bypass
|
|
// the strict containment policy (i.e., UIKit guarantees that every ancestor
|
|
// of the hit view will return YES from -pointInside:withEvent:). See:
|
|
// - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
|
|
for (UIView *subview in [sortedSubviews reverseObjectEnumerator]) {
|
|
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
|
|
hitSubview = [subview hitTest:convertedPoint withEvent:event];
|
|
if (hitSubview != nil) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hitSubview;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RNSFullWindowOverlay {
|
|
__weak RCTBridge *_bridge;
|
|
RNSFullWindowOverlayContainer *_container;
|
|
CGRect _reactFrame;
|
|
#ifdef RN_FABRIC_ENABLED
|
|
RCTSurfaceTouchHandler *_touchHandler;
|
|
#else
|
|
RCTTouchHandler *_touchHandler;
|
|
#endif // RN_FABRIC_ENABLED
|
|
}
|
|
|
|
#ifdef RN_FABRIC_ENABLED
|
|
- (instancetype)init
|
|
{
|
|
if (self = [super init]) {
|
|
static const auto defaultProps = std::make_shared<const facebook::react::RNSFullWindowOverlayProps>();
|
|
_props = defaultProps;
|
|
[self _initCommon];
|
|
}
|
|
return self;
|
|
}
|
|
#endif // RN_FABRIC_ENABLED
|
|
|
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
|
{
|
|
if (self = [super init]) {
|
|
_bridge = bridge;
|
|
[self _initCommon];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)_initCommon
|
|
{
|
|
_reactFrame = CGRectNull;
|
|
_container = self.container;
|
|
[self show];
|
|
}
|
|
|
|
- (void)addSubview:(UIView *)view
|
|
{
|
|
[_container addSubview:view];
|
|
}
|
|
|
|
- (RNSFullWindowOverlayContainer *)container
|
|
{
|
|
if (_container == nil) {
|
|
_container = [[RNSFullWindowOverlayContainer alloc] initWithFrame:_reactFrame];
|
|
}
|
|
|
|
return _container;
|
|
}
|
|
|
|
- (void)show
|
|
{
|
|
UIWindow *window = RCTSharedApplication().delegate.window;
|
|
[window addSubview:_container];
|
|
}
|
|
|
|
- (void)didMoveToWindow
|
|
{
|
|
if (self.window == nil) {
|
|
if (_container != nil) {
|
|
[_container removeFromSuperview];
|
|
[_touchHandler detachFromView:_container];
|
|
}
|
|
} else {
|
|
if (_touchHandler == nil) {
|
|
#ifdef RN_FABRIC_ENABLED
|
|
_touchHandler = [RCTSurfaceTouchHandler new];
|
|
#else
|
|
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
|
#endif
|
|
}
|
|
[_touchHandler attachToView:_container];
|
|
}
|
|
}
|
|
|
|
#ifdef RN_FABRIC_ENABLED
|
|
#pragma mark - Fabric Specific
|
|
|
|
// When the component unmounts we remove it from window's children,
|
|
// so when the component gets recycled we need to add it back.
|
|
- (void)maybeShow
|
|
{
|
|
UIWindow *window = RCTSharedApplication().delegate.window;
|
|
if (![[window subviews] containsObject:self]) {
|
|
[window addSubview:_container];
|
|
}
|
|
}
|
|
|
|
+ (facebook::react::ComponentDescriptorProvider)componentDescriptorProvider
|
|
{
|
|
return facebook::react::concreteComponentDescriptorProvider<
|
|
facebook::react::RNSFullWindowOverlayComponentDescriptor>();
|
|
}
|
|
|
|
- (void)prepareForRecycle
|
|
{
|
|
[_container removeFromSuperview];
|
|
// Due to view recycling we don't really want to set _container = nil
|
|
// as it won't be instantiated when the component appears for the second time.
|
|
// We could consider nulling in here & using container (lazy getter) everywhere else.
|
|
// _container = nil;
|
|
[super prepareForRecycle];
|
|
}
|
|
|
|
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
|
{
|
|
// When the component unmounts we remove it from window's children,
|
|
// so when the component gets recycled we need to add it back.
|
|
// As for now it is called here as we lack of method that is called
|
|
// just before component gets restored (from recycle pool).
|
|
[self maybeShow];
|
|
[self addSubview:childComponentView];
|
|
}
|
|
|
|
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
|
{
|
|
[childComponentView removeFromSuperview];
|
|
}
|
|
|
|
- (void)updateLayoutMetrics:(facebook::react::LayoutMetrics const &)layoutMetrics
|
|
oldLayoutMetrics:(facebook::react::LayoutMetrics const &)oldLayoutMetrics
|
|
{
|
|
CGRect frame = RCTCGRectFromRect(layoutMetrics.frame);
|
|
_reactFrame = frame;
|
|
[_container setFrame:frame];
|
|
}
|
|
|
|
#else
|
|
#pragma mark - Paper specific
|
|
|
|
- (void)reactSetFrame:(CGRect)frame
|
|
{
|
|
_reactFrame = frame;
|
|
[_container setFrame:frame];
|
|
}
|
|
|
|
- (void)invalidate
|
|
{
|
|
[_container removeFromSuperview];
|
|
_container = nil;
|
|
}
|
|
|
|
#endif // RN_FABRIC_ENABLED
|
|
|
|
@end
|
|
|
|
#ifdef RN_FABRIC_ENABLED
|
|
Class<RCTComponentViewProtocol> RNSFullWindowOverlayCls(void)
|
|
{
|
|
return RNSFullWindowOverlay.class;
|
|
}
|
|
#endif // RN_FABRIC_ENABLED
|
|
|
|
@implementation RNSFullWindowOverlayManager
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
#ifdef RN_FABRIC_ENABLED
|
|
#else
|
|
- (UIView *)view
|
|
{
|
|
return [[RNSFullWindowOverlay alloc] initWithBridge:self.bridge];
|
|
}
|
|
#endif // RN_FABRIC_ENABLED
|
|
|
|
@end
|