From efdd8acd99f4442265f57b1c0907aea8a03813b0 Mon Sep 17 00:00:00 2001 From: Ben Dolman Date: Tue, 6 Oct 2020 10:36:43 -0600 Subject: [PATCH] Fix hit point when ASCollectionNode inverted set to true (#1781) * Account for possible inverted transform during hit test When ASCollectionNode has the `inverted` flag set, a transform gets set on the cell node. We need to make sure that we account for that when dealing in the view coordinates. * Store self.node and self.node.view in local variables for better readability. * Add a test for hit testing in an inverted ASCollectionNode --- Source/Details/_ASCollectionViewCell.mm | 10 +++++++--- Tests/ASCollectionViewTests.mm | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Source/Details/_ASCollectionViewCell.mm b/Source/Details/_ASCollectionViewCell.mm index 28d8cec2a..93f4626a4 100644 --- a/Source/Details/_ASCollectionViewCell.mm +++ b/Source/Details/_ASCollectionViewCell.mm @@ -97,6 +97,9 @@ - (void)layoutSubviews - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + ASCellNode *node = self.node; + UIView *nodeView = node.view; + /** * The documentation for hitTest:withEvent: on an UIView explicitly states the fact that: * it ignores view objects that are hidden, that have disabled user interactions, or have an @@ -106,12 +109,13 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event * superclass hitTest:withEvent: implementation. If this returns a valid value we can go on with * checking the node as it's expected to not be in one of these states. */ - if (![super hitTest:self.bounds.origin withEvent:event]) { + CGPoint originPointOnView = [self convertPoint:nodeView.bounds.origin fromView:nodeView]; + if (![super hitTest:originPointOnView withEvent:event]) { return nil; } - CGPoint pointOnNode = [self.node.view convertPoint:point fromView:self]; - return [self.node hitTest:pointOnNode withEvent:event]; + CGPoint pointOnNode = [node.view convertPoint:point fromView:self]; + return [node hitTest:pointOnNode withEvent:event]; } - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index 496c1feba..ab4779ea9 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -387,6 +387,31 @@ - (void)testThatCollectionNodeConformsToExpectedProtocols XCTAssert([node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]); } +/** + * Test that hit tests are correct when the collection node is inverted. + */ +- (void)testInvertedCollectionViewHitTest +{ + ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; + UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [window setRootViewController:testController]; + [window makeKeyAndVisible]; + + testController.collectionNode.inverted = true; + [testController.collectionNode reloadData]; + [testController.collectionNode waitUntilAllUpdatesAreProcessed]; + [testController.collectionView layoutIfNeeded]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + UICollectionViewCell *cell = [testController.collectionView cellForItemAtIndexPath:indexPath]; + ASDisplayNode *node = [testController.collectionNode nodeForItemAtIndexPath:indexPath]; + + CGPoint testPointInCollectionView = CGPointMake(CGRectGetMidX(cell.frame), CGRectGetMidY(cell.frame)); + UIView *hitTestView = [testController.collectionView hitTest:testPointInCollectionView withEvent:nil]; + + XCTAssertEqualObjects(hitTestView, node.view, @"Expected node's view to be the result of the hit test."); +} + #pragma mark - Update Validations #define updateValidationTestPrologue \