diff --git a/AsyncDisplayKit/ASControlNode.mm b/AsyncDisplayKit/ASControlNode.mm index 9a2fd276a4..3fc2f43e57 100644 --- a/AsyncDisplayKit/ASControlNode.mm +++ b/AsyncDisplayKit/ASControlNode.mm @@ -467,8 +467,7 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v // Even if our parents don't have clipsToBounds set and would allow us to display the debug overlay, UIKit event delivery (hitTest:) // will not search sub-hierarchies if one of our parents does not return YES for pointInside:. In such a scenario, hitTestSlop // may not be able to expand the tap target as much as desired without also setting some hitTestSlop on the limiting parents. - CGRect originalRect = UIEdgeInsetsInsetRect(self.bounds, [self hitTestSlop]); - CGRect intersectRect = originalRect; + CGRect intersectRect = UIEdgeInsetsInsetRect(self.bounds, [self hitTestSlop]); UIRectEdge clippedEdges = UIRectEdgeNone; UIRectEdge clipsToBoundsClippedEdges = UIRectEdgeNone; CALayer *layer = self.layer; @@ -480,18 +479,33 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v while (intersectSuperlayer && ![intersectSuperlayer.delegate respondsToSelector:@selector(contentOffset)]) { // Get our parent's tappable bounds. If the parent has an associated node, consider hitTestSlop, as it will extend its pointInside:. CGRect parentHitRect = intersectSuperlayer.bounds; - BOOL parentClipsToBounds = intersectSuperlayer.masksToBounds; + BOOL parentClipsToBounds = NO; + ASDisplayNode *parentNode = ASLayerToDisplayNode(intersectSuperlayer); - if (parentNode && !parentClipsToBounds) { - parentHitRect = UIEdgeInsetsInsetRect(parentHitRect, [parentNode hitTestSlop]); + if (parentNode) { + UIEdgeInsets parentSlop = [parentNode hitTestSlop]; + + // if parent has a hitTestSlop as well, we need to account for the fact that events will be routed towards us in that area too. + if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, parentSlop)) { + parentClipsToBounds = parentNode.clipsToBounds; + // if the parent is clipping, this will prevent us from showing the overlay outside that area. + // in this case, we will make the overlay smaller so that the special highlight to indicate the overlay + // cannot accurately display the true tappable area is shown. + if (!parentClipsToBounds) { + parentHitRect = UIEdgeInsetsInsetRect(parentHitRect, [parentNode hitTestSlop]); + } + } } // Convert our current rectangle to parent coordinates, and intersect with the parent's hit rect. CGRect intersectRectInParentCoordinates = [intersectSuperlayer convertRect:intersectRect fromLayer:intersectLayer]; intersectRect = CGRectIntersection(parentHitRect, intersectRectInParentCoordinates); - if (parentClipsToBounds) { - if (!CGSizeEqualToSize(parentHitRect.size, intersectRectInParentCoordinates.size)) { - clipsToBoundsClippedEdges = [self setEdgesOfIntersectionForChildRect:intersectRectInParentCoordinates parentRect:parentHitRect rectEdge:clipsToBoundsClippedEdges]; + if (!CGSizeEqualToSize(parentHitRect.size, intersectRectInParentCoordinates.size)) { + clippedEdges = [self setEdgesOfIntersectionForChildRect:intersectRectInParentCoordinates + parentRect:parentHitRect rectEdge:clippedEdges]; + if (parentClipsToBounds) { + clipsToBoundsClippedEdges = [self setEdgesOfIntersectionForChildRect:intersectRectInParentCoordinates + parentRect:parentHitRect rectEdge:clipsToBoundsClippedEdges]; } } @@ -504,10 +518,7 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v UIColor *fillColor = [[UIColor greenColor] colorWithAlphaComponent:0.4]; // determine which edges were clipped - if (!CGSizeEqualToSize(originalRect.size, finalRect.size)) { - - clippedEdges = [self setEdgesOfIntersectionForChildRect:originalRect parentRect:finalRect rectEdge:clippedEdges]; - + if (clippedEdges != UIRectEdgeNone) { const CGFloat borderWidth = 2.0; UIColor *superhitTestSlopClipsBorderColor = [UIColor colorWithRed:30/255.0 green:90/255.0 blue:50/255.0 alpha:0.7]; UIColor *superClipsToBoundsBorderColor = [[UIColor orangeColor] colorWithAlphaComponent:0.8]; diff --git a/AsyncDisplayKit/AsyncDisplayKit+Debug.h b/AsyncDisplayKit/AsyncDisplayKit+Debug.h index ba6897b9b0..b2667d1e65 100644 --- a/AsyncDisplayKit/AsyncDisplayKit+Debug.h +++ b/AsyncDisplayKit/AsyncDisplayKit+Debug.h @@ -12,9 +12,12 @@ @interface ASControlNode (Debugging) /** - Class method to enable a visualization overlay of the tapable area on the ASControlNode. For app debugging purposes only. - Overlay = translucent green color, edges clipped by hitTestDebug of any parent in the hierarchy = dark green bordered edge, - edges clipped by clipToBounds = YES of any parent in the hierarchy = orange bordered edge. + Class method to enable a visualization overlay of the tappable area on the ASControlNode. For app debugging purposes only. + NOTE: GESTURE RECOGNIZERS, (including tap gesture recognizers on a control node) WILL NOT BE VISUALIZED!!! + Overlay = translucent GREEN color, + edges that are clipped by the tappable area of any parent (their bounds + hitTestSlop) in the hierarchy = DARK GREEN BORDERED EDGE, + edges that are clipped by clipToBounds = YES of any parent in the hierarchy = ORANGE BORDERED EDGE (may still receive touches beyond + overlay rect, but can't be visualized). @param enable Specify YES to make this debug feature enabled when messaging the ASControlNode class. */ + (void)setEnableHitTestDebug:(BOOL)enable;