Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fabric] Clean up hit testing now that RCTUIView extends RCTPlatformView #1814

Merged
merged 2 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions React/Base/RCTUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,6 @@ CGPathRef UIBezierPathCreateCGPathRef(UIBezierPath *path);
- (void)layoutIfNeeded;

- (void)layoutSubviews;
- (NSArray<RCTUIView *> *)reactZIndexSortedSubviews; // [macOS]
lenaic marked this conversation as resolved.
Show resolved Hide resolved

- (void)setNeedsDisplay;

Expand All @@ -414,10 +413,6 @@ CGPathRef UIBezierPathCreateCGPathRef(UIBezierPath *path);
* Specifies whether focus ring should be drawn when the view has the first responder status.
*/
@property (nonatomic, assign) BOOL enableFocusRing;
/**
* The z-index of the view.
*/
@property (nonatomic, assign) NSInteger reactZIndex;

@end

Expand Down
21 changes: 0 additions & 21 deletions React/Base/macOS/RCTUIKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -406,27 +406,6 @@ - (void)layoutSubviews
[super layout];
}

- (NSArray<RCTUIView *> *)reactZIndexSortedSubviews
{
// Check if sorting is required - in most cases it won't be.
BOOL sortingRequired = NO;
for (RCTUIView *subview in self.subviews) {
if (subview.reactZIndex != 0) {
sortingRequired = YES;
break;
}
}
return sortingRequired ? [self.subviews sortedArrayUsingComparator:^NSComparisonResult(RCTUIView *a, RCTUIView *b) {
if (a.reactZIndex > b.reactZIndex) {
return NSOrderedDescending;
} else {
// Ensure sorting is stable by treating equal zIndex as ascending so
// that original order is preserved.
return NSOrderedAscending;
}
}] : self.subviews;
}

- (void)setNeedsDisplay
{
self.needsDisplay = YES;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,6 @@ - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet<NSString *
return _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
}

#if !TARGET_OS_OSX // [macOS]
- (RCTUIView *)betterHitTest:(CGPoint)point withEvent:(UIEvent *)event // [macOS]
{
// This is a classic textbook implementation of `hitTest:` with a couple of improvements:
Expand Down Expand Up @@ -509,60 +508,6 @@ - (RCTUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event // [macOS]
return view != self ? view : nil;
}
}
#else // [macOS

- (RCTUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event // [macOS]
{
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).
RCTUIView *hitSubview = nil; // [macOS]
BOOL isPointInside = [self pointInside:point withEvent:event];
BOOL needsHitSubview = !(_props->pointerEvents == PointerEventsMode::None || _props->pointerEvents == PointerEventsMode::BoxOnly);
if (needsHitSubview && (![self clipsToBounds] || isPointInside)) {
// Take z-index into account when calculating the touch target.
NSArray<RCTUIView *> *sortedSubviews = [self reactZIndexSortedSubviews]; // [macOS]

// 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 (RCTUIView *subview in [sortedSubviews reverseObjectEnumerator]) { // [macOS]
CGPoint pointForHitTest = CGPointZero;
if ([subview isKindOfClass:[RCTUIView class]]) { // [macOS]
pointForHitTest = [subview convertPoint:point fromView:self];
} else {
pointForHitTest = point;
}
hitSubview = (RCTUIView *)[subview hitTest:pointForHitTest]; // [macOS]
if (hitSubview != nil) {
break;
}
}
}

RCTUIView *hitView = (isPointInside ? self : nil); // [macOS]

switch (_props->pointerEvents) {
case PointerEventsMode::None:
return nil;
case PointerEventsMode::Auto:
return hitSubview ?: hitView;
case PointerEventsMode::BoxOnly:
return hitView;
case PointerEventsMode::BoxNone:
return hitSubview;
default:
return hitSubview ?: hitView;
}
}
#endif // macOS]

static RCTCornerRadii RCTCornerRadiiFromBorderRadii(BorderRadii borderRadii)
{
Expand Down
8 changes: 8 additions & 0 deletions React/Views/UIView+React.m
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,14 @@ - (void)setAccessibilityValueInternal:(NSDictionary<NSString *, id> *)accessibil
self, @selector(accessibilityValueInternal), accessibilityValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

// [macOS
#pragma mark - Hit testing
- (RCTPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return [self hitTest:point];
}
// macOS]

#pragma mark - Debug
- (void)react_addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
{
Expand Down