diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 85bd1dd6..5c78d177 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -33,6 +33,8 @@ - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect { [self pushGlyphContext]; + + __block CGRect groupRect = CGRectNull; [self traverseSubviews:^(UIView *node) { if ([node isKindOfClass:[RNSVGNode class]]) { @@ -46,6 +48,11 @@ } [svgNode renderTo:context rect:rect]; + + CGRect nodeRect = svgNode.clientRect; + if (!CGRectIsEmpty(nodeRect)) { + groupRect = CGRectUnion(groupRect, nodeRect); + } if ([node isKindOfClass:[RNSVGRenderable class]]) { [(RNSVGRenderable*)node resetProperties]; @@ -62,6 +69,7 @@ return YES; }]; [self setHitArea:[self getPath:context]]; + self.clientRect = groupRect; [self popGlyphContext]; } diff --git a/ios/Elements/RNSVGSvgView.h b/ios/Elements/RNSVGSvgView.h index 88eabdca..524c395a 100644 --- a/ios/Elements/RNSVGSvgView.h +++ b/ios/Elements/RNSVGSvgView.h @@ -25,6 +25,9 @@ @property (nonatomic, assign) RNSVGVBMOS meetOrSlice; @property (nonatomic, assign) BOOL responsible; @property (nonatomic, assign) CGRect boundingBox; +@property (nonatomic, assign) CGAffineTransform initialCTM; +@property (nonatomic, assign) CGAffineTransform invInitialCTM; + /** diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index 17045ec4..833c8441 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -135,6 +135,8 @@ - (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect { + self.initialCTM = CGContextGetCTM(context); + self.invInitialCTM = CGAffineTransformInvert(self.initialCTM); if (self.align) { _viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) eRect:rect @@ -147,7 +149,8 @@ for (UIView *node in self.subviews) { if ([node isKindOfClass:[RNSVGNode class]]) { RNSVGNode *svg = (RNSVGNode *)node; - [svg renderTo:context rect:rect]; + [svg renderTo:context + rect:rect]; } else { [node drawRect:rect]; } diff --git a/ios/Elements/RNSVGUse.m b/ios/Elements/RNSVGUse.m index cd9dcf87..f2118293 100644 --- a/ios/Elements/RNSVGUse.m +++ b/ios/Elements/RNSVGUse.m @@ -49,6 +49,7 @@ // TODO: calling yellow box here RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href); } + self.clientRect = template.clientRect; } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index b0762d47..d90e6a33 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -35,6 +35,9 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; @property (nonatomic, assign) CGAffineTransform invmatrix; @property (nonatomic, assign) BOOL active; @property (nonatomic, assign) CGPathRef path; +@property (nonatomic, assign) CGRect clientRect; +@property (nonatomic, copy) RCTDirectEventBlock onLayout; + /** * RNSVGSvgView which ownes current RNSVGNode diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 34609503..8aeac5aa 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -145,6 +145,24 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; [container invalidate]; } +- (void)setClientRect:(CGRect)clientRect { + if (CGRectEqualToRect(_clientRect, clientRect)) { + return; + } + _clientRect = clientRect; + if (self.onLayout) { + self.onLayout(@{ + @"layout": @{ + @"x": @(_clientRect.origin.x), + @"y": @(_clientRect.origin.y), + @"width": @(_clientRect.size.width), + @"height": @(_clientRect.size.height), + } + }); + + } +} + - (void)setClipPath:(NSString *)clipPath { if (_clipPath == clipPath) { diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index b6821200..98ee006e 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -187,6 +187,10 @@ self.path = CGPathRetain(CFAutorelease(CGPathCreateCopy([self getPath:context]))); [self setHitArea:self.path]; } + + const CGRect pathBounding = CGPathGetBoundingBox(self.path); + const CGAffineTransform svgToClientTransform = CGAffineTransformConcat(CGContextGetCTM(context), self.svgView.invInitialCTM); + self.clientRect = CGRectApplyAffineTransform(pathBounding, svgToClientTransform); CGPathDrawingMode mode = kCGPathStroke; BOOL fillColor = NO; diff --git a/ios/ViewManagers/RNSVGNodeManager.m b/ios/ViewManagers/RNSVGNodeManager.m index 5cda8ca4..50015ddc 100644 --- a/ios/ViewManagers/RNSVGNodeManager.m +++ b/ios/ViewManagers/RNSVGNodeManager.m @@ -35,5 +35,7 @@ RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform) RCT_EXPORT_VIEW_PROPERTY(clipPath, NSString) RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule) RCT_EXPORT_VIEW_PROPERTY(responsible, BOOL) +RCT_EXPORT_VIEW_PROPERTY(onLayout, RCTDirectEventBlock) + @end diff --git a/lib/extract/extractProps.js b/lib/extract/extractProps.js index aa138cd5..6f5def49 100644 --- a/lib/extract/extractProps.js +++ b/lib/extract/extractProps.js @@ -10,7 +10,8 @@ export default function(props, ref) { const extractedProps = { opacity: extractOpacity(props.opacity), - propList: styleProperties + propList: styleProperties, + onLayout: props.onLayout }; if (props.id) { diff --git a/lib/props.js b/lib/props.js index a39e071e..93b24956 100644 --- a/lib/props.js +++ b/lib/props.js @@ -18,6 +18,10 @@ const touchableProps = { delayLongPress: PropTypes.number }; +const layoutProps = { + onLayout: PropTypes.func +}; + const responderProps = [ ...Object.keys(PanResponder.create({}).panHandlers), "pointerEvents" @@ -79,6 +83,7 @@ const pathProps = { ...transformProps, ...responderProps, ...touchableProps, + ...layoutProps, ...definationProps };