#import #import "RNSFullWindowOverlay.h" #ifdef RCT_NEW_ARCH_ENABLED #import #import #import #import #import #import #else #import #endif // RCT_NEW_ARCH_ENABLED @implementation RNSFullWindowOverlayContainer - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.accessibilityViewIsModal = YES; } return self; } - (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 *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 RCT_NEW_ARCH_ENABLED RCTSurfaceTouchHandler *_touchHandler; #else RCTTouchHandler *_touchHandler; #endif // RCT_NEW_ARCH_ENABLED } #ifdef RCT_NEW_ARCH_ENABLED - (instancetype)init { if (self = [super init]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; [self _initCommon]; } return self; } #endif // RCT_NEW_ARCH_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 = RCTKeyWindow(); [window addSubview:_container]; } - (void)didMoveToSuperview { if (self.superview == nil) { if (_container != nil) { [_container removeFromSuperview]; [_touchHandler detachFromView:_container]; } } else { if (_container != nil) { UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, _container); } if (_touchHandler == nil) { #ifdef RCT_NEW_ARCH_ENABLED _touchHandler = [RCTSurfaceTouchHandler new]; #else _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; #endif } [_touchHandler attachToView:_container]; } } #ifdef RCT_NEW_ARCH_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 = RCTKeyWindow(); if (![[window subviews] containsObject:self]) { [window addSubview:_container]; } } + (react::ComponentDescriptorProvider)componentDescriptorProvider { return react::concreteComponentDescriptorProvider(); } - (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 *)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 *)childComponentView index:(NSInteger)index { [childComponentView removeFromSuperview]; } - (void)updateLayoutMetrics:(react::LayoutMetrics const &)layoutMetrics oldLayoutMetrics:(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 // RCT_NEW_ARCH_ENABLED @end #ifdef RCT_NEW_ARCH_ENABLED Class RNSFullWindowOverlayCls(void) { return RNSFullWindowOverlay.class; } #endif // RCT_NEW_ARCH_ENABLED @implementation RNSFullWindowOverlayManager RCT_EXPORT_MODULE() #ifdef RCT_NEW_ARCH_ENABLED #else - (UIView *)view { return [[RNSFullWindowOverlay alloc] initWithBridge:self.bridge]; } #endif // RCT_NEW_ARCH_ENABLED @end