diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 803a209a..fc75689a 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -74,9 +74,13 @@ return YES; }]; - [self setHitArea:[self getPath:context]]; + CGPathRef path = [self getPath:context]; + [self setHitArea:path]; if (!CGRectEqualToRect(bounds, CGRectNull)) { self.clientRect = bounds; + const CGRect fillBounds = CGPathGetBoundingBox(path); + const CGRect strokeBounds = CGPathGetBoundingBox(self.strokePath); + self.pathBounds = CGRectUnion(fillBounds, strokeBounds); CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); @@ -150,6 +154,10 @@ CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); transformed = CGPointApplyAffineTransform(transformed, self.invTransform); + if (!CGRectContainsPoint(self.pathBounds, transformed)) { + return nil; + } + if (self.clipPath) { RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath]; if ([clipNode isSimpleClipPath]) { @@ -169,7 +177,7 @@ NSPredicate *const anyActive = [NSPredicate predicateWithFormat:@"active == TRUE"]; NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive]; if ([filtered count] != 0) { - return [filtered.firstObject hitTest:transformed withEvent:event]; + return [filtered.lastObject hitTest:transformed withEvent:event]; } } diff --git a/ios/Elements/RNSVGImage.m b/ios/Elements/RNSVGImage.m index 1f4c80ba..3d8dc444 100644 --- a/ios/Elements/RNSVGImage.m +++ b/ios/Elements/RNSVGImage.m @@ -117,6 +117,7 @@ CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil); [self setHitArea:hitAreaPath]; CGPathRelease(hitAreaPath); + self.pathBounds = hitArea; // apply viewBox transform on Image render. CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height); diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index 5ff803bb..bb94d891 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -40,7 +40,9 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; @property (nonatomic, assign) BOOL dirty; @property (nonatomic, assign) BOOL merging; @property (nonatomic, assign) CGPathRef path; +@property (nonatomic, assign) CGPathRef strokePath; @property (nonatomic, assign) CGRect clientRect; +@property (nonatomic, assign) CGRect pathBounds; @property (nonatomic, copy) RCTDirectEventBlock onLayout; diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 0086e58d..7d491e21 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -550,6 +550,7 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (void)dealloc { CGPathRelease(_cachedClipPath); + CGPathRelease(_strokePath); CGImageRelease(_clipMask); CGPathRelease(_path); _clipMask = nil; diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 37741ffc..d8067aae 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -18,7 +18,6 @@ NSArray *_attributeList; NSArray *_sourceStrokeDashArray; CGFloat *_strokeDashArrayData; - CGPathRef _strokePath; CGPathRef _srcHitPath; CGPathRef _hitArea; } @@ -157,7 +156,6 @@ - (void)dealloc { CGPathRelease(_hitArea); - CGPathRelease(_strokePath); _sourceStrokeDashArray = nil; if (_strokeDashArrayData) { free(_strokeDashArrayData); @@ -295,15 +293,15 @@ UInt32 saturate(CGFloat value) { self.path = CGPathRetain(path); } [self setHitArea:path]; + const CGRect fillBounds = CGPathGetBoundingBox(path); + const CGRect strokeBounds = CGPathGetBoundingBox(self.strokePath); + self.pathBounds = CGRectUnion(fillBounds, strokeBounds); } - - const CGRect fillBounds = CGPathGetBoundingBox(path); - const CGRect strokeBounds = CGPathGetBoundingBox(_strokePath); - const CGRect pathBounding = CGRectUnion(fillBounds, strokeBounds); + const CGRect pathBounds = self.pathBounds; CGAffineTransform current = CGContextGetCTM(context); CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); - CGRect clientRect = CGRectApplyAffineTransform(pathBounding, svgToClientTransform); + CGRect clientRect = CGRectApplyAffineTransform(pathBounds, svgToClientTransform); self.clientRect = clientRect; @@ -312,7 +310,7 @@ UInt32 saturate(CGFloat value) { CGAffineTransform matrix = CGAffineTransformConcat(transform, vbmatrix); CGRect bounds = CGRectMake(0, 0, CGRectGetWidth(clientRect), CGRectGetHeight(clientRect)); - CGPoint mid = CGPointMake(CGRectGetMidX(pathBounding), CGRectGetMidY(pathBounding)); + CGPoint mid = CGPointMake(CGRectGetMidX(pathBounds), CGRectGetMidY(pathBounds)); CGPoint center = CGPointApplyAffineTransform(mid, matrix); self.bounds = bounds; @@ -352,7 +350,7 @@ UInt32 saturate(CGFloat value) { [self.fill paint:context opacity:self.fillOpacity painter:[self.svgView getDefinedPainter:self.fill.brushRef] - bounds:pathBounding + bounds:pathBounds ]; CGContextRestoreGState(context); @@ -409,7 +407,7 @@ UInt32 saturate(CGFloat value) { [self.stroke paint:context opacity:self.strokeOpacity painter:[self.svgView getDefinedPainter:self.stroke.brushRef] - bounds:pathBounding + bounds:pathBounds ]; return; } @@ -426,13 +424,13 @@ UInt32 saturate(CGFloat value) { } _srcHitPath = path; CGPathRelease(_hitArea); - CGPathRelease(_strokePath); + CGPathRelease(self.strokePath); _hitArea = CGPathCreateCopy(path); - _strokePath = nil; + self.strokePath = nil; if (self.stroke && self.strokeWidth) { // Add stroke to hitArea CGFloat width = [self relativeOnOther:self.strokeWidth]; - _strokePath = CGPathRetain(CFAutorelease(CGPathCreateCopyByStrokingPath(path, nil, width, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit))); + self.strokePath = CGPathRetain(CFAutorelease(CGPathCreateCopyByStrokingPath(path, nil, width, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit))); // TODO add dashing // CGPathCreateCopyByDashingPath(CGPathRef _Nullable path, const CGAffineTransform * _Nullable transform, CGFloat phase, const CGFloat * _Nullable lengths, size_t count) } @@ -460,9 +458,13 @@ UInt32 saturate(CGFloat value) { CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); transformed = CGPointApplyAffineTransform(transformed, self.invTransform); + if (!CGRectContainsPoint(self.pathBounds, transformed)) { + return nil; + } + BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd; if (!CGPathContainsPoint(_hitArea, nil, transformed, evenodd) && - !CGPathContainsPoint(_strokePath, nil, transformed, NO)) { + !CGPathContainsPoint(self.strokePath, nil, transformed, NO)) { return nil; }