@@ -124,6 +124,7 @@ - (instancetype)initWithFrame:(CGRect)frame
124
124
_borderBottomStartRadius = -1 ;
125
125
_borderBottomEndRadius = -1 ;
126
126
_borderStyle = RCTBorderStyleSolid;
127
+ _hitTestEdgeInsets = NSEdgeInsetsZero ;
127
128
self.clipsToBounds = NO ;
128
129
}
129
130
@@ -186,21 +187,71 @@ - (void)setTransform:(CATransform3D)transform
186
187
187
188
- (NSView *)hitTest : (CGPoint)point
188
189
{
189
- // TODO: implement pointerEvents
190
+ // TODO: implement "isUserInteractionEnabled"
191
+ // BOOL canReceiveTouchEvents = ([self isUserInteractionEnabled] && ![self isHidden]);
192
+ // if(!canReceiveTouchEvents) {
193
+ // return nil;
194
+ // }
195
+
196
+ if (self.isHidden ) {
197
+ return nil ;
198
+ }
199
+
200
+ // `hitSubview` is the topmost subview which was hit. The hit point can
201
+ // be outside the bounds of `view` (e.g., if -clipsToBounds is NO).
202
+ NSView *hitSubview = nil ;
203
+ BOOL isPointInside = [self pointInside: point];
204
+ BOOL needsHitSubview = !(_pointerEvents == RCTPointerEventsNone || _pointerEvents == RCTPointerEventsBoxOnly);
205
+ if (needsHitSubview && (![self clipsToBounds ] || isPointInside)) {
206
+ // Take z-index into account when calculating the touch target.
207
+ NSArray <NSView *> *sortedSubviews = [self reactZIndexSortedSubviews ];
208
+
209
+ // The default behaviour of UIKit is that if a view does not contain a point,
210
+ // then no subviews will be returned from hit testing, even if they contain
211
+ // the hit point. By doing hit testing directly on the subviews, we bypass
212
+ // the strict containment policy (i.e., UIKit guarantees that every ancestor
213
+ // of the hit view will return YES from -pointInside:withEvent:). See:
214
+ // - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
215
+ for (NSView *subview in [sortedSubviews reverseObjectEnumerator ]) {
216
+ CGPoint convertedPoint = [subview convertPoint: point fromView: self ];
217
+ hitSubview = [subview hitTest: convertedPoint];
218
+ if (hitSubview != nil ) {
219
+ break ;
220
+ }
221
+ }
222
+ }
223
+
224
+ NSView *hitView = (isPointInside ? self : nil );
225
+ return hitSubview ?: hitView;
226
+
227
+ // TODO: implement "pointerEvents"
190
228
// switch (_pointerEvents) {
191
229
// case RCTPointerEventsNone:
192
230
// return nil;
193
231
// case RCTPointerEventsUnspecified:
194
- // return RCTViewHitTest(self, point, event) ?: [super hitTest:point withEvent:event] ;
232
+ // return hitSubview ?: hitView ;
195
233
// case RCTPointerEventsBoxOnly:
196
- // return [super hitTest:point withEvent:event] ? self: nil ;
234
+ // return hitView ;
197
235
// case RCTPointerEventsBoxNone:
198
- // return RCTViewHitTest(self, point, event) ;
236
+ // return hitSubview ;
199
237
// default:
200
- // RCTLogError(@"Invalid pointer-events specified %zd on %@", _pointerEvents, self);
201
- // return [super hitTest:point withEvent:event] ;
238
+ // RCTLogError(@"Invalid pointer-events specified %lld on %@", (long long) _pointerEvents, self);
239
+ // return hitSubview ?: hitView ;
202
240
// }
203
- return [super hitTest: point];
241
+ }
242
+
243
+ static inline CGRect NSEdgeInsetsInsetRect (CGRect rect, NSEdgeInsets insets) {
244
+ rect.origin .x += insets.left ;
245
+ rect.origin .y += insets.top ;
246
+ rect.size .width -= (insets.left + insets.right );
247
+ rect.size .height -= (insets.top + insets.bottom );
248
+ return rect;
249
+ }
250
+
251
+ - (BOOL )pointInside : (CGPoint)point
252
+ {
253
+ CGRect hitFrame = NSEdgeInsetsInsetRect(self.bounds , self.hitTestEdgeInsets );
254
+ return CGRectContainsPoint (hitFrame, point);
204
255
}
205
256
206
257
- (NSView *)reactAccessibilityElement
0 commit comments