|
9 | 9 | #import "ASControlNode.h" |
10 | 10 | #import "ASControlNode+Subclasses.h" |
11 | 11 | #import "ASThread.h" |
| 12 | +#import "ASDisplayNodeExtras.h" |
| 13 | +#import "ASImageNode.h" |
12 | 14 |
|
13 | 15 | // UIControl allows dragging some distance outside of the control itself during |
14 | 16 | // tracking. This value depends on the device idiom (25 or 70 points), so |
@@ -73,7 +75,7 @@ @interface ASControlNode () |
73 | 75 |
|
74 | 76 | @implementation ASControlNode |
75 | 77 | { |
76 | | - ASDisplayNode *_debugHighlightOverlay; |
| 78 | + ASImageNode *_debugHighlightOverlay; |
77 | 79 | } |
78 | 80 |
|
79 | 81 | #pragma mark - Lifecycle |
@@ -251,10 +253,10 @@ - (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeE |
251 | 253 |
|
252 | 254 | // add a highlight overlay node with area of ASControlNode + UIEdgeInsets |
253 | 255 | self.clipsToBounds = NO; |
254 | | - _debugHighlightOverlay = [[ASDisplayNode alloc] init]; |
| 256 | + _debugHighlightOverlay = [[ASImageNode alloc] init]; |
| 257 | + _debugHighlightOverlay.zPosition = 1000; // CALayer doesn't have -moveSublayerToFront, but this will ensure we're over the top of any siblings. |
255 | 258 | _debugHighlightOverlay.layerBacked = YES; |
256 | | - _debugHighlightOverlay.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.5]; |
257 | | - |
| 259 | + _debugHighlightOverlay.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.4]; |
258 | 260 | [self addSubnode:_debugHighlightOverlay]; |
259 | 261 | } |
260 | 262 | } |
@@ -461,12 +463,35 @@ - (void)layout |
461 | 463 | [super layout]; |
462 | 464 |
|
463 | 465 | if (_debugHighlightOverlay) { |
464 | | - UIEdgeInsets insets = [self hitTestSlop]; |
465 | | - CGRect controlNodeRect = self.bounds; |
466 | | - _debugHighlightOverlay.frame = CGRectMake(controlNodeRect.origin.x + insets.left, |
467 | | - controlNodeRect.origin.y + insets.top, |
468 | | - controlNodeRect.size.width - insets.left - insets.right, |
469 | | - controlNodeRect.size.height - insets.top - insets.bottom); |
| 466 | + |
| 467 | + // Even if our parents don't have clipsToBounds set and would allow us to display the debug overlay, UIKit event delivery (hitTest:) |
| 468 | + // will not search sub-hierarchies if one of our parents does not return YES for pointInside:. In such a scenario, hitTestSlop |
| 469 | + // may not be able to expand the tap target as much as desired without also setting some hitTestSlop on the limiting parents. |
| 470 | + CGRect intersectRect = UIEdgeInsetsInsetRect(self.bounds, [self hitTestSlop]); |
| 471 | + CALayer *layer = self.layer; |
| 472 | + CALayer *intersectLayer = layer; |
| 473 | + CALayer *intersectSuperlayer = layer.superlayer; |
| 474 | + |
| 475 | + // Stop climbing if we encounter a UIScrollView, as its offset bounds origin may make it seem like our events will be clipped when |
| 476 | + // scrolling will actually reveal them (because this process will not re-run due to scrolling) |
| 477 | + while (intersectSuperlayer && ![intersectSuperlayer.delegate respondsToSelector:@selector(contentOffset)]) { |
| 478 | + // Get our parent's tappable bounds. If the parent has an associated node, consider hitTestSlop, as it will extend its pointInside:. |
| 479 | + CGRect parentHitRect = intersectSuperlayer.bounds; |
| 480 | + ASDisplayNode *parentNode = ASLayerToDisplayNode(intersectSuperlayer); |
| 481 | + if (parentNode) { |
| 482 | + parentHitRect = UIEdgeInsetsInsetRect(parentHitRect, [parentNode hitTestSlop]); |
| 483 | + } |
| 484 | + |
| 485 | + // Convert our current rectangle to parent coordinates, and intersect with the parent's hit rect. |
| 486 | + CGRect intersectRectInParentCoordinates = [intersectSuperlayer convertRect:intersectRect fromLayer:intersectLayer]; |
| 487 | + intersectRect = CGRectIntersection(parentHitRect, intersectRectInParentCoordinates); |
| 488 | + |
| 489 | + // Advance up the tree. |
| 490 | + intersectLayer = intersectSuperlayer; |
| 491 | + intersectSuperlayer = intersectLayer.superlayer; |
| 492 | + } |
| 493 | + |
| 494 | + _debugHighlightOverlay.frame = [intersectLayer convertRect:intersectRect toLayer:layer]; |
470 | 495 | } |
471 | 496 | } |
472 | 497 |
|
|
0 commit comments