Skip to content

Commit

Permalink
Small refactor to make RCTView more readable
Browse files Browse the repository at this point in the history
Summary:
Fixes some comment typos, moves hit testing and accessibility code so it's beside each other.

No functionality changes.

Changelog:[Internal]

Reviewed By: RSNara

Differential Revision: D22003047

fbshipit-source-id: 0e785364d7e1a080034d24c9676a56acb45686bb
  • Loading branch information
Peter Argany authored and facebook-github-bot committed Jun 11, 2020
1 parent fb8411d commit 952c03b
Showing 1 changed file with 77 additions and 72 deletions.
149 changes: 77 additions & 72 deletions React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,76 @@ - (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
}
}

#pragma mark - Hit Testing

- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
{
_pointerEvents = pointerEvents;
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
if (pointerEvents == RCTPointerEventsBoxNone) {
self.accessibilityViewIsModal = 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];
BOOL needsHitSubview = !(_pointerEvents == RCTPointerEventsNone || _pointerEvents == RCTPointerEventsBoxOnly);
if (needsHitSubview && (![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;
}
}
}

UIView *hitView = (isPointInside ? self : nil);

switch (_pointerEvents) {
case RCTPointerEventsNone:
return nil;
case RCTPointerEventsUnspecified:
return hitSubview ?: hitView;
case RCTPointerEventsBoxOnly:
return hitView;
case RCTPointerEventsBoxNone:
return hitSubview;
default:
RCTLogError(@"Invalid pointer-events specified %lld on %@", (long long)_pointerEvents, self);
return hitSubview ?: hitView;
}
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
return [super pointInside:point withEvent:event];
}
CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}

#pragma mark - Accessibility

- (NSString *)accessibilityLabel
{
NSString *label = super.accessibilityLabel;
Expand Down Expand Up @@ -186,10 +256,8 @@ - (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)acti
if (!_onAccessibilityAction || !accessibilityActionsLabelMap) {
return NO;
}

// iOS defines the name as the localized label, so use our map to convert this back to the non-localized action namne
// iOS defines the name as the localized label, so use our map to convert this back to the non-localized action name
// when passing to JS. This allows for standard action names across platforms.

NSDictionary *actionObject = accessibilityActionsLabelMap[action.name];
if (actionObject) {
_onAccessibilityAction(@{@"actionName" : actionObject[@"name"], @"actionTarget" : self.reactTag});
Expand All @@ -216,6 +284,7 @@ - (NSString *)accessibilityValue
}
}
if (rolesAndStatesDescription == nil) {
// Falling back to hardcoded English list.
NSLog(@"Cannot load localized accessibility strings.");
rolesAndStatesDescription = @{
@"alert" : @"alert",
Expand Down Expand Up @@ -244,6 +313,7 @@ - (NSString *)accessibilityValue
}
});

// Handle Switch.
if ((self.accessibilityTraits & SwitchAccessibilityTrait) == SwitchAccessibilityTrait) {
for (NSString *state in self.accessibilityState) {
id val = self.accessibilityState[state];
Expand All @@ -260,6 +330,8 @@ - (NSString *)accessibilityValue
if (roleDescription) {
[valueComponents addObject:roleDescription];
}

// Handle states which haven't already been handled in RCTViewManager.
for (NSString *state in self.accessibilityState) {
id val = self.accessibilityState[state];
if (!val) {
Expand All @@ -280,8 +352,7 @@ - (NSString *)accessibilityValue
}
}

// handle accessibilityValue

// Handle accessibilityValue.
if (self.accessibilityValueInternal) {
id min = self.accessibilityValueInternal[@"min"];
id now = self.accessibilityValueInternal[@"now"];
Expand All @@ -304,72 +375,6 @@ - (NSString *)accessibilityValue
return nil;
}

- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
{
_pointerEvents = pointerEvents;
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
if (pointerEvents == RCTPointerEventsBoxNone) {
self.accessibilityViewIsModal = 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];
BOOL needsHitSubview = !(_pointerEvents == RCTPointerEventsNone || _pointerEvents == RCTPointerEventsBoxOnly);
if (needsHitSubview && (![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;
}
}
}

UIView *hitView = (isPointInside ? self : nil);

switch (_pointerEvents) {
case RCTPointerEventsNone:
return nil;
case RCTPointerEventsUnspecified:
return hitSubview ?: hitView;
case RCTPointerEventsBoxOnly:
return hitView;
case RCTPointerEventsBoxNone:
return hitSubview;
default:
RCTLogError(@"Invalid pointer-events specified %lld on %@", (long long)_pointerEvents, self);
return hitSubview ?: hitView;
}
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
return [super pointInside:point withEvent:event];
}
CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}

- (UIView *)reactAccessibilityElement
{
return self;
Expand Down Expand Up @@ -492,7 +497,7 @@ + (UIEdgeInsets)contentInsetsForView:(UIView *)view
return UIEdgeInsetsZero;
}

#pragma mark - View unmounting
#pragma mark - View Unmounting

- (void)react_remountAllSubviews
{
Expand Down

0 comments on commit 952c03b

Please sign in to comment.