From a5ee11a36230af9c9f26b0e7064c3587be56c0aa Mon Sep 17 00:00:00 2001 From: Nishan Date: Fri, 9 Feb 2024 23:48:52 +0530 Subject: [PATCH 01/10] wip --- .../Libraries/StyleSheet/processTransform.js | 8 ++ .../React/Views/RCTConvert+Transform.h | 2 + .../React/Views/RCTConvert+Transform.m | 117 ++++++++++++++++++ .../react-native/React/Views/RCTViewManager.m | 2 +- .../react-native/React/Views/UIView+React.h | 2 +- .../react-native/React/Views/UIView+React.m | 24 ++-- 6 files changed, 143 insertions(+), 12 deletions(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index a703ac47161462..195d186a5a525b 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -256,6 +256,14 @@ function _validateTransform( break; case 'translateX': case 'translateY': + invariant( + typeof value === 'number' || + (typeof value === 'string' && value.endsWith('%')), + 'Transform with key of "%s" must be number or a percentage. Passed value: %s.', + key, + stringifySafe(transformation), + ); + break; case 'scale': case 'scaleX': case 'scaleY': diff --git a/packages/react-native/React/Views/RCTConvert+Transform.h b/packages/react-native/React/Views/RCTConvert+Transform.h index 68e6bba0b863eb..b20e7eaa06db79 100644 --- a/packages/react-native/React/Views/RCTConvert+Transform.h +++ b/packages/react-native/React/Views/RCTConvert+Transform.h @@ -12,6 +12,8 @@ + (CATransform3D)CATransform3D:(id)json; ++ (CATransform3D)CATransform3D:(id)json withWidth:(CGFloat)width height:(CGFloat)height; + + (RCTTransformOrigin)RCTTransformOrigin:(id)json; @end diff --git a/packages/react-native/React/Views/RCTConvert+Transform.m b/packages/react-native/React/Views/RCTConvert+Transform.m index 8248b5892a6f38..e0131bd8b33c17 100644 --- a/packages/react-native/React/Views/RCTConvert+Transform.m +++ b/packages/react-native/React/Views/RCTConvert+Transform.m @@ -144,6 +144,123 @@ + (CATransform3D)CATransform3D:(id)json return transform; } ++ (CATransform3D)CATransform3D:(id)json withWidth:(CGFloat)width height:(CGFloat)height { + { + CATransform3D transform = CATransform3DIdentity; + if (!json) { + return transform; + } + if (![json isKindOfClass:[NSArray class]]) { + RCTLogConvertError(json, @"a CATransform3D. Did you pass something other than an array?"); + return transform; + } + // legacy matrix support + if ([(NSArray *)json count] == kMatrixArrayLength && [json[0] isKindOfClass:[NSNumber class]]) { + RCTLogWarn( + @"[RCTConvert CATransform3D:] has deprecated a matrix as input. Pass an array of configs (which can contain a matrix key) instead."); + return [self CATransform3DFromMatrix:json]; + } + + CGFloat zeroScaleThreshold = FLT_EPSILON; + + CATransform3D next; + for (NSDictionary *transformConfig in (NSArray *)json) { + if (transformConfig.count != 1) { + RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object."); + return transform; + } + NSString *property = transformConfig.allKeys[0]; + id value = transformConfig[property]; + + if ([property isEqualToString:@"matrix"]) { + next = [self CATransform3DFromMatrix:value]; + transform = CATransform3DConcat(next, transform); + + } else if ([property isEqualToString:@"perspective"]) { + next = CATransform3DIdentity; + next.m34 = -1 / [value floatValue]; + transform = CATransform3DConcat(next, transform); + + } else if ([property isEqualToString:@"rotateX"]) { + CGFloat rotate = [self convertToRadians:value]; + transform = CATransform3DRotate(transform, rotate, 1, 0, 0); + + } else if ([property isEqualToString:@"rotateY"]) { + CGFloat rotate = [self convertToRadians:value]; + transform = CATransform3DRotate(transform, rotate, 0, 1, 0); + + } else if ([property isEqualToString:@"rotate"] || [property isEqualToString:@"rotateZ"]) { + CGFloat rotate = [self convertToRadians:value]; + transform = CATransform3DRotate(transform, rotate, 0, 0, 1); + + } else if ([property isEqualToString:@"scale"]) { + CGFloat scale = [value floatValue]; + scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; + transform = CATransform3DScale(transform, scale, scale, 1); + + } else if ([property isEqualToString:@"scaleX"]) { + CGFloat scale = [value floatValue]; + scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; + transform = CATransform3DScale(transform, scale, 1, 1); + + } else if ([property isEqualToString:@"scaleY"]) { + CGFloat scale = [value floatValue]; + scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; + transform = CATransform3DScale(transform, 1, scale, 1); + + } else if ([property isEqualToString:@"translate"]) { + + NSArray *array = (NSArray *)value; + CGFloat translateX = [array[0] floatValue]; + CGFloat translateY = [array[1] floatValue]; + CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0; + transform = CATransform3DTranslate(transform, translateX, translateY, translateZ); + + } else if ([property isEqualToString:@"translateX"]) { + CGFloat translate = [self convertValue:value withDimension:width]; + transform = CATransform3DTranslate(transform, translate, 0, 0); + + } else if ([property isEqualToString:@"translateY"]) { + CGFloat translate = [self convertValue:value withDimension:height]; + transform = CATransform3DTranslate(transform, 0, translate, 0); + + } else if ([property isEqualToString:@"skewX"]) { + CGFloat skew = [self convertToRadians:value]; + next = CATransform3DIdentity; + next.m21 = tanf(skew); + transform = CATransform3DConcat(next, transform); + + } else if ([property isEqualToString:@"skewY"]) { + CGFloat skew = [self convertToRadians:value]; + next = CATransform3DIdentity; + next.m12 = tanf(skew); + transform = CATransform3DConcat(next, transform); + + } else { + RCTLogInfo(@"Unsupported transform type for a CATransform3D: %@.", property); + } + } + return transform; + } +} + ++ (CGFloat)convertValue:(id)value withDimension:(CGFloat)dimension { + if ([value isKindOfClass:[NSNumber class]]) { + return [value floatValue]; + } else if ([value isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)value; + if ([stringValue hasSuffix:@"%"]) { + NSString *percentageString = [stringValue substringToIndex:stringValue.length - 1]; + CGFloat percentage = [percentageString floatValue] / 100.0f; + return dimension * percentage; + } else { + return [stringValue floatValue]; + } + } + return 0; +} + + + (RCTTransformOrigin)RCTTransformOrigin:(id)json { RCTTransformOrigin transformOrigin = { diff --git a/packages/react-native/React/Views/RCTViewManager.m b/packages/react-native/React/Views/RCTViewManager.m index 66c449d7e81a00..59785490bd2976 100644 --- a/packages/react-native/React/Views/RCTViewManager.m +++ b/packages/react-native/React/Views/RCTViewManager.m @@ -216,7 +216,7 @@ - (RCTShadowView *)shadowView view.layer.shouldRasterize ? view.traitCollection.displayScale : defaultView.layer.rasterizationScale; } -RCT_REMAP_VIEW_PROPERTY(transform, reactTransform, CATransform3D) +RCT_REMAP_VIEW_PROPERTY(transform, reactTransform, NSArray) RCT_REMAP_VIEW_PROPERTY(transformOrigin, reactTransformOrigin, RCTTransformOrigin) RCT_CUSTOM_VIEW_PROPERTY(accessibilityRole, UIAccessibilityTraits, RCTView) diff --git a/packages/react-native/React/Views/UIView+React.h b/packages/react-native/React/Views/UIView+React.h index fbc8ca0f94c80d..c91e13ad34c50e 100644 --- a/packages/react-native/React/Views/UIView+React.h +++ b/packages/react-native/React/Views/UIView+React.h @@ -115,7 +115,7 @@ typedef struct { * The anchorPoint property doesn't work in the same way as on web - updating it updates the frame. * To work around this, we take both the transform and the transform-origin, and compute it ourselves */ -@property (nonatomic, assign) CATransform3D reactTransform; +@property (nonatomic, copy) NSArray* reactTransform; @property (nonatomic, assign) RCTTransformOrigin reactTransformOrigin; /** diff --git a/packages/react-native/React/Views/UIView+React.m b/packages/react-native/React/Views/UIView+React.m index 2ee2c500ddb7bf..bbc30127774fa1 100644 --- a/packages/react-native/React/Views/UIView+React.m +++ b/packages/react-native/React/Views/UIView+React.m @@ -12,6 +12,7 @@ #import "RCTAssert.h" #import "RCTLog.h" #import "RCTShadowView.h" +#import "RCTConvert+Transform.h" @implementation UIView (React) @@ -205,22 +206,23 @@ - (void)reactSetFrame:(CGRect)frame self.bounds = bounds; id transformOrigin = objc_getAssociatedObject(self, @selector(reactTransformOrigin)); - if (transformOrigin) { + id transform = objc_getAssociatedObject(self, @selector(reactTransform)); + if (transformOrigin || transform) { updateTransform(self); } } #pragma mark - Transforms -- (CATransform3D)reactTransform +- (NSArray*)reactTransform { id obj = objc_getAssociatedObject(self, _cmd); - return obj != nil ? [obj CATransform3DValue] : CATransform3DIdentity; + return obj; } -- (void)setReactTransform:(CATransform3D)reactTransform +- (void)setReactTransform:(NSArray*)reactTransform { - objc_setAssociatedObject(self, @selector(reactTransform), @(reactTransform), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(self, @selector(reactTransform), reactTransform, OBJC_ASSOCIATION_RETAIN_NONATOMIC); updateTransform(self); } @@ -245,10 +247,14 @@ - (void)setReactTransformOrigin:(RCTTransformOrigin)reactTransformOrigin static void updateTransform(UIView *view) { - CATransform3D transform; + CGSize size = view.bounds.size; + if (CGSizeEqualToSize(CGSizeZero, size)) { + return; + }; + + CATransform3D transform = [RCTConvert CATransform3D: view.reactTransform withWidth: size.width height:size.height]; id rawTansformOrigin = objc_getAssociatedObject(view, @selector(reactTransformOrigin)); if (rawTansformOrigin) { - CGSize size = view.bounds.size; CGFloat anchorPointX = 0; CGFloat anchorPointY = 0; CGFloat anchorPointZ = 0; @@ -267,11 +273,9 @@ static void updateTransform(UIView *view) } anchorPointZ = transformOrigin.z; transform = CATransform3DConcat( - view.reactTransform, CATransform3DMakeTranslation(anchorPointX, anchorPointY, anchorPointZ)); + transform, CATransform3DMakeTranslation(anchorPointX, anchorPointY, anchorPointZ)); transform = CATransform3DConcat(CATransform3DMakeTranslation(-anchorPointX, -anchorPointY, -anchorPointZ), transform); - } else { - transform = view.reactTransform; } view.layer.transform = transform; From 9fcca39cdffe567fa450018e8df808a1e6c94f77 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 09:57:18 +0530 Subject: [PATCH 02/10] wip --- packages/react-native/React/Views/RCTConvert+Transform.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native/React/Views/RCTConvert+Transform.m b/packages/react-native/React/Views/RCTConvert+Transform.m index e0131bd8b33c17..51c5d7ca2d3fb2 100644 --- a/packages/react-native/React/Views/RCTConvert+Transform.m +++ b/packages/react-native/React/Views/RCTConvert+Transform.m @@ -217,11 +217,11 @@ + (CATransform3D)CATransform3D:(id)json withWidth:(CGFloat)width height:(CGFloat transform = CATransform3DTranslate(transform, translateX, translateY, translateZ); } else if ([property isEqualToString:@"translateX"]) { - CGFloat translate = [self convertValue:value withDimension:width]; + CGFloat translate = [self parseTranslate:value withDimension:width]; transform = CATransform3DTranslate(transform, translate, 0, 0); } else if ([property isEqualToString:@"translateY"]) { - CGFloat translate = [self convertValue:value withDimension:height]; + CGFloat translate = [self parseTranslate:value withDimension:height]; transform = CATransform3DTranslate(transform, 0, translate, 0); } else if ([property isEqualToString:@"skewX"]) { @@ -244,7 +244,7 @@ + (CATransform3D)CATransform3D:(id)json withWidth:(CGFloat)width height:(CGFloat } } -+ (CGFloat)convertValue:(id)value withDimension:(CGFloat)dimension { ++ (CGFloat)parseTranslate:(id)value withDimension:(CGFloat)dimension { if ([value isKindOfClass:[NSNumber class]]) { return [value floatValue]; } else if ([value isKindOfClass:[NSString class]]) { From 927809d78bea8fc24013646a410b7ad93e8f962f Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 18:21:53 +0530 Subject: [PATCH 03/10] translate regex to support % --- .../React/Views/RCTConvert+Transform.m | 7 +++---- .../js/examples/Transform/TransformExample.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/react-native/React/Views/RCTConvert+Transform.m b/packages/react-native/React/Views/RCTConvert+Transform.m index 51c5d7ca2d3fb2..f8acd384ec67ce 100644 --- a/packages/react-native/React/Views/RCTConvert+Transform.m +++ b/packages/react-native/React/Views/RCTConvert+Transform.m @@ -209,10 +209,9 @@ + (CATransform3D)CATransform3D:(id)json withWidth:(CGFloat)width height:(CGFloat transform = CATransform3DScale(transform, 1, scale, 1); } else if ([property isEqualToString:@"translate"]) { - - NSArray *array = (NSArray *)value; - CGFloat translateX = [array[0] floatValue]; - CGFloat translateY = [array[1] floatValue]; + NSArray *array = (NSArray *)value; + CGFloat translateX = [self parseTranslate:array[0] withDimension:width]; + CGFloat translateY = [self parseTranslate:array[1] withDimension:height]; CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0; transform = CATransform3DTranslate(transform, translateX, translateY, translateZ); diff --git a/packages/rn-tester/js/examples/Transform/TransformExample.js b/packages/rn-tester/js/examples/Transform/TransformExample.js index 75370c931cc47c..b163d8ba1587ce 100644 --- a/packages/rn-tester/js/examples/Transform/TransformExample.js +++ b/packages/rn-tester/js/examples/Transform/TransformExample.js @@ -139,6 +139,10 @@ function Flip() { ); } +function TranslatePercentage() { + return ; +} + const styles = StyleSheet.create({ container: { height: 500, @@ -277,6 +281,12 @@ const styles = StyleSheet.create({ height: 100, transformOrigin: 'top left', }, + translatePercentageView: { + transform: 'translate(50%)', + padding: 50, + alignSelf: 'flex-start', + backgroundColor: 'lightblue', + }, }); exports.title = 'Transforms'; @@ -396,4 +406,11 @@ exports.examples = [ return ; }, }, + { + title: 'Translate Percentage', + description: "transform: 'translate(50%)'", + render(): Node { + return ; + }, + }, ]; From f0dba3b050eb9a8a11675c55f9f20890bf3a2ddb Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 18:22:18 +0530 Subject: [PATCH 04/10] translate regex to support % --- .../react-native/Libraries/StyleSheet/processTransform.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index 195d186a5a525b..3eba9a50fe3423 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -69,7 +69,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateY'>, args: string, ) => {key: string, value?: number[] | number | string} = (key, args) => { - const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+)?/g); + const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z%]+)?/g); switch (key) { case 'matrix': @@ -88,7 +88,11 @@ const _getKeyAndValueFromCSSTransform: ( missingUnitOfMeasurement = true; } - parsedArgs.push(value); + if (unitOfMeasurement === '%') { + parsedArgs.push(`${value}%`); + } else { + parsedArgs.push(value); + } } if (__DEV__) { From b49a38d29703e93d2c09615c4db7aa0c47291772 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 19:28:52 +0530 Subject: [PATCH 05/10] translate percentage test --- .../__tests__/__snapshots__/processTransform-test.js.snap | 2 +- .../Libraries/StyleSheet/__tests__/processTransform-test.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap b/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap index dc8d045189ea5e..119c7344a130ba 100644 --- a/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap +++ b/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap @@ -32,7 +32,7 @@ exports[`processTransform validation should throw when passing an invalid angle exports[`processTransform validation should throw when passing an invalid angle prop 4`] = `"Rotate transform must be expressed in degrees (deg) or radians (rad): {\\"skewX\\":\\"10drg\\"}"`; -exports[`processTransform validation should throw when passing an invalid value to a number prop 1`] = `"Transform with key of \\"translateY\\" must be a number: {\\"translateY\\":\\"20deg\\"}"`; +exports[`processTransform validation should throw when passing an invalid value to a number prop 1`] = `"Transform with key of \\"translateY\\" must be number or a percentage. Passed value: {\\"translateY\\":\\"20deg\\"}."`; exports[`processTransform validation should throw when passing an invalid value to a number prop 2`] = `"Transform with key of \\"scale\\" must be a number: {\\"scale\\":{\\"x\\":10,\\"y\\":10}}"`; diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js b/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js index 66ce708c033897..fe7c5d2851505f 100644 --- a/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js +++ b/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js @@ -34,6 +34,11 @@ describe('processTransform', () => { ); }); + it('should accept a percentage translate transform', () => { + processTransform([{translateY: '20%'}, {translateX: '10%'}]); + processTransform('translateX(10%)'); + }); + it('should throw on object with multiple properties', () => { expect(() => processTransform([{scale: 0.5, translateY: 10}]), From d02bd1a841a020c7977dde98518b420612775b97 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 00:56:29 +0530 Subject: [PATCH 06/10] pr feedback and optimisations --- .../React/Views/RCTConvert+Transform.m | 99 +------------------ .../react-native/React/Views/UIView+React.h | 2 +- .../react-native/React/Views/UIView+React.m | 14 +-- 3 files changed, 14 insertions(+), 101 deletions(-) diff --git a/packages/react-native/React/Views/RCTConvert+Transform.m b/packages/react-native/React/Views/RCTConvert+Transform.m index f8acd384ec67ce..9456211519154c 100644 --- a/packages/react-native/React/Views/RCTConvert+Transform.m +++ b/packages/react-native/React/Views/RCTConvert+Transform.m @@ -48,100 +48,7 @@ + (CATransform3D)CATransform3DFromMatrix:(id)json + (CATransform3D)CATransform3D:(id)json { - CATransform3D transform = CATransform3DIdentity; - if (!json) { - return transform; - } - if (![json isKindOfClass:[NSArray class]]) { - RCTLogConvertError(json, @"a CATransform3D. Did you pass something other than an array?"); - return transform; - } - // legacy matrix support - if ([(NSArray *)json count] == kMatrixArrayLength && [json[0] isKindOfClass:[NSNumber class]]) { - RCTLogWarn( - @"[RCTConvert CATransform3D:] has deprecated a matrix as input. Pass an array of configs (which can contain a matrix key) instead."); - return [self CATransform3DFromMatrix:json]; - } - - CGFloat zeroScaleThreshold = FLT_EPSILON; - - CATransform3D next; - for (NSDictionary *transformConfig in (NSArray *)json) { - if (transformConfig.count != 1) { - RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object."); - return transform; - } - NSString *property = transformConfig.allKeys[0]; - id value = transformConfig[property]; - - if ([property isEqualToString:@"matrix"]) { - next = [self CATransform3DFromMatrix:value]; - transform = CATransform3DConcat(next, transform); - - } else if ([property isEqualToString:@"perspective"]) { - next = CATransform3DIdentity; - next.m34 = -1 / [value floatValue]; - transform = CATransform3DConcat(next, transform); - - } else if ([property isEqualToString:@"rotateX"]) { - CGFloat rotate = [self convertToRadians:value]; - transform = CATransform3DRotate(transform, rotate, 1, 0, 0); - - } else if ([property isEqualToString:@"rotateY"]) { - CGFloat rotate = [self convertToRadians:value]; - transform = CATransform3DRotate(transform, rotate, 0, 1, 0); - - } else if ([property isEqualToString:@"rotate"] || [property isEqualToString:@"rotateZ"]) { - CGFloat rotate = [self convertToRadians:value]; - transform = CATransform3DRotate(transform, rotate, 0, 0, 1); - - } else if ([property isEqualToString:@"scale"]) { - CGFloat scale = [value floatValue]; - scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; - transform = CATransform3DScale(transform, scale, scale, 1); - - } else if ([property isEqualToString:@"scaleX"]) { - CGFloat scale = [value floatValue]; - scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; - transform = CATransform3DScale(transform, scale, 1, 1); - - } else if ([property isEqualToString:@"scaleY"]) { - CGFloat scale = [value floatValue]; - scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; - transform = CATransform3DScale(transform, 1, scale, 1); - - } else if ([property isEqualToString:@"translate"]) { - NSArray *array = (NSArray *)value; - CGFloat translateX = [array[0] floatValue]; - CGFloat translateY = [array[1] floatValue]; - CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0; - transform = CATransform3DTranslate(transform, translateX, translateY, translateZ); - - } else if ([property isEqualToString:@"translateX"]) { - CGFloat translate = [value floatValue]; - transform = CATransform3DTranslate(transform, translate, 0, 0); - - } else if ([property isEqualToString:@"translateY"]) { - CGFloat translate = [value floatValue]; - transform = CATransform3DTranslate(transform, 0, translate, 0); - - } else if ([property isEqualToString:@"skewX"]) { - CGFloat skew = [self convertToRadians:value]; - next = CATransform3DIdentity; - next.m21 = tanf(skew); - transform = CATransform3DConcat(next, transform); - - } else if ([property isEqualToString:@"skewY"]) { - CGFloat skew = [self convertToRadians:value]; - next = CATransform3DIdentity; - next.m12 = tanf(skew); - transform = CATransform3DConcat(next, transform); - - } else { - RCTLogInfo(@"Unsupported transform type for a CATransform3D: %@.", property); - } - } - return transform; + return [self CATransform3D:json withWidth:NAN height:NAN]; } + (CATransform3D)CATransform3D:(id)json withWidth:(CGFloat)width height:(CGFloat)height { @@ -249,6 +156,10 @@ + (CGFloat)parseTranslate:(id)value withDimension:(CGFloat)dimension { } else if ([value isKindOfClass:[NSString class]]) { NSString *stringValue = (NSString *)value; if ([stringValue hasSuffix:@"%"]) { + if (isnan(dimension)) { + RCTLogConvertError(value, @"a CATransform3D. Expected dimension to be available for percentage calculation."); + return 0; + } NSString *percentageString = [stringValue substringToIndex:stringValue.length - 1]; CGFloat percentage = [percentageString floatValue] / 100.0f; return dimension * percentage; diff --git a/packages/react-native/React/Views/UIView+React.h b/packages/react-native/React/Views/UIView+React.h index c91e13ad34c50e..8a19ee44dd3cc9 100644 --- a/packages/react-native/React/Views/UIView+React.h +++ b/packages/react-native/React/Views/UIView+React.h @@ -115,7 +115,7 @@ typedef struct { * The anchorPoint property doesn't work in the same way as on web - updating it updates the frame. * To work around this, we take both the transform and the transform-origin, and compute it ourselves */ -@property (nonatomic, copy) NSArray* reactTransform; +@property (nonatomic, retain) NSArray* reactTransform; @property (nonatomic, assign) RCTTransformOrigin reactTransformOrigin; /** diff --git a/packages/react-native/React/Views/UIView+React.m b/packages/react-native/React/Views/UIView+React.m index bbc30127774fa1..f0fcfb4147781a 100644 --- a/packages/react-native/React/Views/UIView+React.m +++ b/packages/react-native/React/Views/UIView+React.m @@ -203,12 +203,14 @@ - (void)reactSetFrame:(CGRect)frame } self.center = position; - self.bounds = bounds; - - id transformOrigin = objc_getAssociatedObject(self, @selector(reactTransformOrigin)); - id transform = objc_getAssociatedObject(self, @selector(reactTransform)); - if (transformOrigin || transform) { - updateTransform(self); + + if (!CGRectEqualToRect(self.bounds, bounds)) { + self.bounds = bounds; + id transformOrigin = objc_getAssociatedObject(self, @selector(reactTransformOrigin)); + id transform = objc_getAssociatedObject(self, @selector(reactTransform)); + if (transform || transformOrigin) { + updateTransform(self); + } } } From 1f1d7e151de73d7bb65285b2363fc759ab59a786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nishan=20=28o=5E=E2=96=BD=5Eo=29?= Date: Sun, 31 Mar 2024 01:27:03 +0530 Subject: [PATCH 07/10] Update packages/react-native/Libraries/StyleSheet/processTransform.js Co-authored-by: Pieter De Baets --- packages/react-native/Libraries/StyleSheet/processTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index 3eba9a50fe3423..b88ba2ea6a4ab3 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -69,7 +69,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateY'>, args: string, ) => {key: string, value?: number[] | number | string} = (key, args) => { - const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z%]+)?/g); + const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g); switch (key) { case 'matrix': From bdb49777d995228f70a00e830acbf8833e3c06cc Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 21:24:24 +0530 Subject: [PATCH 08/10] flow check fix --- packages/react-native/Libraries/StyleSheet/processTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index b88ba2ea6a4ab3..49435279de9aa6 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -68,7 +68,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateX'> | $TEMPORARY$string<'translateY'>, args: string, -) => {key: string, value?: number[] | number | string} = (key, args) => { +) => {key: string, value?: number[] | string[] | number | string} = (key, args) => { const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g); switch (key) { From f019c903a486545faecedb3734613da7ad62d5cb Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 21:30:52 +0530 Subject: [PATCH 09/10] prettier fix --- .../react-native/Libraries/StyleSheet/processTransform.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index 49435279de9aa6..bcc870d7778ae4 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -68,7 +68,10 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateX'> | $TEMPORARY$string<'translateY'>, args: string, -) => {key: string, value?: number[] | string[] | number | string} = (key, args) => { +) => {key: string, value?: number[] | string[] | number | string} = ( + key, + args, +) => { const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g); switch (key) { From ae4fc044e043eb9791dd2e6b8e8d19fed3af30e8 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 21:50:16 +0530 Subject: [PATCH 10/10] fix flow check --- packages/react-native/Libraries/StyleSheet/processTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index bcc870d7778ae4..6310a4b5c77550 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -68,7 +68,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateX'> | $TEMPORARY$string<'translateY'>, args: string, -) => {key: string, value?: number[] | string[] | number | string} = ( +) => {key: string, value?: Array | number | string} = ( key, args, ) => {