1#import <UIKit/UIKit.h> 2 3#import "RNSFullWindowOverlay.h" 4 5#ifdef RCT_NEW_ARCH_ENABLED 6#import <React/RCTConversions.h> 7#import <React/RCTFabricComponentsPlugins.h> 8#import <React/RCTSurfaceTouchHandler.h> 9#import <react/renderer/components/rnscreens/ComponentDescriptors.h> 10#import <react/renderer/components/rnscreens/Props.h> 11#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h> 12#else 13#import <React/RCTTouchHandler.h> 14#endif // RCT_NEW_ARCH_ENABLED 15 16@implementation RNSFullWindowOverlayContainer 17 18- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 19{ 20 for (UIView *view in [self subviews]) { 21 if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) { 22 return YES; 23 } 24 } 25 return NO; 26} 27 28- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 29{ 30 BOOL canReceiveTouchEvents = ([self isUserInteractionEnabled] && ![self isHidden]); 31 if (!canReceiveTouchEvents) { 32 return nil; 33 } 34 35 // `hitSubview` is the topmost subview which was hit. The hit point can 36 // be outside the bounds of `view` (e.g., if -clipsToBounds is NO). 37 UIView *hitSubview = nil; 38 BOOL isPointInside = [self pointInside:point withEvent:event]; 39 if (![self clipsToBounds] || isPointInside) { 40 // Take z-index into account when calculating the touch target. 41 NSArray<UIView *> *sortedSubviews = [self reactZIndexSortedSubviews]; 42 43 // The default behaviour of UIKit is that if a view does not contain a point, 44 // then no subviews will be returned from hit testing, even if they contain 45 // the hit point. By doing hit testing directly on the subviews, we bypass 46 // the strict containment policy (i.e., UIKit guarantees that every ancestor 47 // of the hit view will return YES from -pointInside:withEvent:). See: 48 // - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html 49 for (UIView *subview in [sortedSubviews reverseObjectEnumerator]) { 50 CGPoint convertedPoint = [subview convertPoint:point fromView:self]; 51 hitSubview = [subview hitTest:convertedPoint withEvent:event]; 52 if (hitSubview != nil) { 53 break; 54 } 55 } 56 } 57 return hitSubview; 58} 59 60@end 61 62@implementation RNSFullWindowOverlay { 63 __weak RCTBridge *_bridge; 64 RNSFullWindowOverlayContainer *_container; 65 CGRect _reactFrame; 66#ifdef RCT_NEW_ARCH_ENABLED 67 RCTSurfaceTouchHandler *_touchHandler; 68#else 69 RCTTouchHandler *_touchHandler; 70#endif // RCT_NEW_ARCH_ENABLED 71} 72 73#ifdef RCT_NEW_ARCH_ENABLED 74- (instancetype)init 75{ 76 if (self = [super init]) { 77 static const auto defaultProps = std::make_shared<const facebook::react::RNSFullWindowOverlayProps>(); 78 _props = defaultProps; 79 [self _initCommon]; 80 } 81 return self; 82} 83#endif // RCT_NEW_ARCH_ENABLED 84 85- (instancetype)initWithBridge:(RCTBridge *)bridge 86{ 87 if (self = [super init]) { 88 _bridge = bridge; 89 [self _initCommon]; 90 } 91 92 return self; 93} 94 95- (void)_initCommon 96{ 97 _reactFrame = CGRectNull; 98 _container = self.container; 99 [self show]; 100} 101 102- (void)addSubview:(UIView *)view 103{ 104 [_container addSubview:view]; 105} 106 107- (RNSFullWindowOverlayContainer *)container 108{ 109 if (_container == nil) { 110 _container = [[RNSFullWindowOverlayContainer alloc] initWithFrame:_reactFrame]; 111 } 112 113 return _container; 114} 115 116- (void)show 117{ 118 UIWindow *window = RCTSharedApplication().delegate.window; 119 [window addSubview:_container]; 120} 121 122- (void)didMoveToWindow 123{ 124 if (self.window == nil) { 125 if (_container != nil) { 126 [_container removeFromSuperview]; 127 [_touchHandler detachFromView:_container]; 128 } 129 } else { 130 if (_touchHandler == nil) { 131#ifdef RCT_NEW_ARCH_ENABLED 132 _touchHandler = [RCTSurfaceTouchHandler new]; 133#else 134 _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; 135#endif 136 } 137 [_touchHandler attachToView:_container]; 138 } 139} 140 141#ifdef RCT_NEW_ARCH_ENABLED 142#pragma mark - Fabric Specific 143 144// When the component unmounts we remove it from window's children, 145// so when the component gets recycled we need to add it back. 146- (void)maybeShow 147{ 148 UIWindow *window = RCTSharedApplication().delegate.window; 149 if (![[window subviews] containsObject:self]) { 150 [window addSubview:_container]; 151 } 152} 153 154+ (facebook::react::ComponentDescriptorProvider)componentDescriptorProvider 155{ 156 return facebook::react::concreteComponentDescriptorProvider< 157 facebook::react::RNSFullWindowOverlayComponentDescriptor>(); 158} 159 160- (void)prepareForRecycle 161{ 162 [_container removeFromSuperview]; 163 // Due to view recycling we don't really want to set _container = nil 164 // as it won't be instantiated when the component appears for the second time. 165 // We could consider nulling in here & using container (lazy getter) everywhere else. 166 // _container = nil; 167 [super prepareForRecycle]; 168} 169 170- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index 171{ 172 // When the component unmounts we remove it from window's children, 173 // so when the component gets recycled we need to add it back. 174 // As for now it is called here as we lack of method that is called 175 // just before component gets restored (from recycle pool). 176 [self maybeShow]; 177 [self addSubview:childComponentView]; 178} 179 180- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index 181{ 182 [childComponentView removeFromSuperview]; 183} 184 185- (void)updateLayoutMetrics:(facebook::react::LayoutMetrics const &)layoutMetrics 186 oldLayoutMetrics:(facebook::react::LayoutMetrics const &)oldLayoutMetrics 187{ 188 CGRect frame = RCTCGRectFromRect(layoutMetrics.frame); 189 _reactFrame = frame; 190 [_container setFrame:frame]; 191} 192 193#else 194#pragma mark - Paper specific 195 196- (void)reactSetFrame:(CGRect)frame 197{ 198 _reactFrame = frame; 199 [_container setFrame:frame]; 200} 201 202- (void)invalidate 203{ 204 [_container removeFromSuperview]; 205 _container = nil; 206} 207 208#endif // RCT_NEW_ARCH_ENABLED 209 210@end 211 212#ifdef RCT_NEW_ARCH_ENABLED 213Class<RCTComponentViewProtocol> RNSFullWindowOverlayCls(void) 214{ 215 return RNSFullWindowOverlay.class; 216} 217#endif // RCT_NEW_ARCH_ENABLED 218 219@implementation RNSFullWindowOverlayManager 220 221RCT_EXPORT_MODULE() 222 223#ifdef RCT_NEW_ARCH_ENABLED 224#else 225- (UIView *)view 226{ 227 return [[RNSFullWindowOverlay alloc] initWithBridge:self.bridge]; 228} 229#endif // RCT_NEW_ARCH_ENABLED 230 231@end 232