diff --git a/Examples/UIExplorer/js/NativeAnimationsExample.js b/Examples/UIExplorer/js/NativeAnimationsExample.js index 208f27a649d2cc..25c6fd261ff85d 100644 --- a/Examples/UIExplorer/js/NativeAnimationsExample.js +++ b/Examples/UIExplorer/js/NativeAnimationsExample.js @@ -30,8 +30,11 @@ const { Animated, StyleSheet, TouchableWithoutFeedback, + Slider, } = ReactNative; +var AnimatedSlider = Animated.createAnimatedComponent(Slider); + class Tester extends React.Component { state = { native: new Animated.Value(0), @@ -228,7 +231,6 @@ exports.description = 'Test out Native Animations'; exports.examples = [ { title: 'Multistage With Multiply and rotation', - description: 'description', render: function() { return ( Animated.spring', - description: 'description', render: function() { return ( ); }, + },{ + title: 'Drive custom property', + render: function() { + return ( + + {anim => ( + + )} + + ); + }, }, { title: 'Animated value listener', diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index 9c6151acce2ad4..11b91b2509a124 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -1676,7 +1676,6 @@ class AnimatedProps extends Animated { propsConfig[propKey] = value.__getNativeTag(); } } - NativeAnimatedHelper.validateProps(propsConfig); return { type: 'props', props: propsConfig, diff --git a/Libraries/Animated/src/NativeAnimatedHelper.js b/Libraries/Animated/src/NativeAnimatedHelper.js index 168451a47f9715..9949155ec96fe8 100644 --- a/Libraries/Animated/src/NativeAnimatedHelper.js +++ b/Libraries/Animated/src/NativeAnimatedHelper.js @@ -100,22 +100,19 @@ const API = { }; /** - * Properties allowed by the native animated implementation. + * Styles allowed by the native animated implementation. * * In general native animated implementation should support any numeric property that doesn't need - * to be updated through the shadow view hierarchy (all non-layout properties). This list is limited - * to the properties that will perform best when animated off the JS thread. + * to be updated through the shadow view hierarchy (all non-layout properties). */ -const PROPS_WHITELIST = { - style: { - opacity: true, - transform: true, - /* legacy android transform properties */ - scaleX: true, - scaleY: true, - translateX: true, - translateY: true, - }, +const STYLES_WHITELIST = { + opacity: true, + transform: true, + /* legacy android transform properties */ + scaleX: true, + scaleY: true, + translateX: true, + translateY: true, }; const TRANSFORM_WHITELIST = { @@ -130,14 +127,6 @@ const TRANSFORM_WHITELIST = { perspective: true, }; -function validateProps(params: Object): void { - for (var key in params) { - if (!PROPS_WHITELIST.hasOwnProperty(key)) { - throw new Error(`Property '${key}' is not supported by native animated module`); - } - } -} - function validateTransform(configs: Array): void { configs.forEach((config) => { if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) { @@ -147,7 +136,6 @@ function validateTransform(configs: Array): void { } function validateStyles(styles: Object): void { - var STYLES_WHITELIST = PROPS_WHITELIST.style || {}; for (var key in styles) { if (!STYLES_WHITELIST.hasOwnProperty(key)) { throw new Error(`Style property '${key}' is not supported by native animated module`); @@ -188,7 +176,6 @@ function isNativeAnimatedAvailable(): boolean { module.exports = { API, - validateProps, validateStyles, validateTransform, validateInterpolation, diff --git a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h index 5613f61407ac8d..64edebf7681246 100644 --- a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h +++ b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h @@ -14,11 +14,10 @@ @interface RCTPropsAnimatedNode : RCTAnimatedNode -@property (nonatomic, readonly) RCTViewPropertyMapper *propertyMapper; +- (void)connectToView:(NSNumber *)viewTag + viewName:(NSString *)viewName + uiManager:(RCTUIManager *)uiManager; -- (void)connectToView:(NSNumber *)viewTag uiManager:(RCTUIManager *)uiManager; - (void)disconnectFromView:(NSNumber *)viewTag; -- (void)performViewUpdatesIfNecessary; - @end diff --git a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m index a2017405b1c61e..76b146e525e07a 100644 --- a/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m @@ -9,27 +9,33 @@ #import "RCTPropsAnimatedNode.h" +#import +#import + #import "RCTAnimationUtils.h" #import "RCTStyleAnimatedNode.h" #import "RCTValueAnimatedNode.h" -#import "RCTViewPropertyMapper.h" - -@implementation RCTPropsAnimatedNode -- (void)connectToView:(NSNumber *)viewTag uiManager:(RCTUIManager *)uiManager -{ - _propertyMapper = [[RCTViewPropertyMapper alloc] initWithViewTag:viewTag uiManager:uiManager]; +@implementation RCTPropsAnimatedNode { + NSNumber *_connectedViewTag; + NSString *_connectedViewName; + RCTUIManager *_uiManager; } -- (void)disconnectFromView:(NSNumber *)viewTag +- (void)connectToView:(NSNumber *)viewTag + viewName:(NSString *)viewName + uiManager:(RCTUIManager *)uiManager { - _propertyMapper = nil; + _connectedViewTag = viewTag; + _connectedViewName = viewName; + _uiManager = uiManager; } -- (void)performUpdate +- (void)disconnectFromView:(NSNumber *)viewTag { - [super performUpdate]; - [self performViewUpdatesIfNecessary]; + _connectedViewTag = nil; + _connectedViewName = nil; + _uiManager = nil; } - (NSString *)propertyNameForParentTag:(NSNumber *)parentTag @@ -44,8 +50,15 @@ - (NSString *)propertyNameForParentTag:(NSNumber *)parentTag return propertyName; } -- (void)performViewUpdatesIfNecessary +- (void)performUpdate { + [super performUpdate]; + + if (!_connectedViewTag) { + RCTLogError(@"Node has not been attached to a view"); + return; + } + NSMutableDictionary *props = [NSMutableDictionary dictionary]; [self.parentNodes enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull parentTag, RCTAnimatedNode * _Nonnull parentNode, BOOL * _Nonnull stop) { @@ -61,7 +74,9 @@ - (void)performViewUpdatesIfNecessary }]; if (props.count) { - [_propertyMapper updateViewWithDictionary:props]; + [_uiManager synchronouslyUpdateViewOnUIThread:_connectedViewTag + viewName:_connectedViewName + props:props]; } } diff --git a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m index 07a89344b5d6df..91b681fdc395d2 100644 --- a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m @@ -33,14 +33,12 @@ - (void)performUpdate { [super performUpdate]; - CATransform3D transform = CATransform3DIdentity; - NSArray *transformConfigs = self.config[@"transforms"]; + NSMutableArray *transform = [NSMutableArray arrayWithCapacity:transformConfigs.count]; for (NSDictionary *transformConfig in transformConfigs) { NSString *type = transformConfig[@"type"]; NSString *property = transformConfig[@"property"]; - - CGFloat value; + NSNumber *value; if ([type isEqualToString: @"animated"]) { NSNumber *nodeTag = transformConfig[@"nodeTag"]; RCTAnimatedNode *node = self.parentNodes[nodeTag]; @@ -48,41 +46,14 @@ - (void)performUpdate continue; } RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node; - value = parentNode.value; + value = @(parentNode.value); } else { - value = [transformConfig[@"value"] floatValue]; - } - - if ([property isEqualToString:@"scale"]) { - transform = CATransform3DScale(transform, value, value, 1); - - } else if ([property isEqualToString:@"scaleX"]) { - transform = CATransform3DScale(transform, value, 1, 1); - - } else if ([property isEqualToString:@"scaleY"]) { - transform = CATransform3DScale(transform, 1, value, 1); - - } else if ([property isEqualToString:@"translateX"]) { - transform = CATransform3DTranslate(transform, value, 0, 0); - - } else if ([property isEqualToString:@"translateY"]) { - transform = CATransform3DTranslate(transform, 0, value, 0); - - } else if ([property isEqualToString:@"rotate"]) { - transform = CATransform3DRotate(transform, value, 0, 0, 1); - - } else if ([property isEqualToString:@"rotateX"]) { - transform = CATransform3DRotate(transform, value, 1, 0, 0); - - } else if ([property isEqualToString:@"rotateY"]) { - transform = CATransform3DRotate(transform, value, 0, 1, 0); - - } else if ([property isEqualToString:@"perspective"]) { - transform.m34 = 1.0 / -value; + value = transformConfig[@"value"]; } + [transform addObject:@{property: value}]; } - _propsDictionary[@"transform"] = [NSValue valueWithCATransform3D:transform]; + _propsDictionary[@"transform"] = transform; } @end diff --git a/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj b/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj index 06d377870d510b..5150f0d3522d46 100644 --- a/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj +++ b/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 13E501CC1D07A644005F35D8 /* RCTAnimationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */; }; 13E501CF1D07A644005F35D8 /* RCTNativeAnimatedModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */; }; - 13E501D41D07A644005F35D8 /* RCTViewPropertyMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */; }; 13E501E81D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */; }; 13E501E91D07A6C9005F35D8 /* RCTAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */; }; 13E501EB1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */; }; @@ -22,7 +21,6 @@ 19F00F221DC8847500113FEE /* RCTEventAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F00F211DC8847500113FEE /* RCTEventAnimation.m */; }; 19F00F231DC8848E00113FEE /* RCTEventAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F00F211DC8847500113FEE /* RCTEventAnimation.m */; }; 2D3B5EF21D9B0B3100451313 /* RCTAnimationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */; }; - 2D3B5EF31D9B0B3400451313 /* RCTViewPropertyMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */; }; 2D3B5EF41D9B0B3700451313 /* RCTNativeAnimatedModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */; }; 2D3B5EF51D9B0B4800451313 /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; }; 2D3B5EF61D9B0B4800451313 /* RCTDiffClampAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */; }; @@ -51,9 +49,7 @@ 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationUtils.m; sourceTree = ""; }; 13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTNativeAnimatedModule.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNativeAnimatedModule.m; sourceTree = ""; }; - 13E501C71D07A644005F35D8 /* RCTViewPropertyMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewPropertyMapper.h; sourceTree = ""; }; - 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTViewPropertyMapper.m; sourceTree = ""; }; - 13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTAdditionAnimatedNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAdditionAnimatedNode.h; sourceTree = ""; }; 13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAdditionAnimatedNode.m; sourceTree = ""; }; 13E501D81D07A6C9005F35D8 /* RCTAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimatedNode.h; sourceTree = ""; }; 13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimatedNode.m; sourceTree = ""; }; @@ -130,8 +126,6 @@ children = ( 13E501B71D07A644005F35D8 /* RCTAnimationUtils.h */, 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */, - 13E501C71D07A644005F35D8 /* RCTViewPropertyMapper.h */, - 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */, 13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */, 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */, 94DA09161DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.h */, @@ -245,7 +239,6 @@ 2D3B5EFF1D9B0B4800451313 /* RCTTransformAnimatedNode.m in Sources */, 2D3B5EFC1D9B0B4800451313 /* RCTMultiplicationAnimatedNode.m in Sources */, 2D3B5EFD1D9B0B4800451313 /* RCTPropsAnimatedNode.m in Sources */, - 2D3B5EF31D9B0B3400451313 /* RCTViewPropertyMapper.m in Sources */, 944244D01DB962DA0032A02B /* RCTFrameAnimation.m in Sources */, 944244D11DB962DC0032A02B /* RCTSpringAnimation.m in Sources */, 9476E8EC1DC9232D005D5CD1 /* RCTNativeAnimatedNodesManager.m in Sources */, @@ -273,7 +266,6 @@ 13E501E81D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m in Sources */, 5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */, 13E501EF1D07A6C9005F35D8 /* RCTTransformAnimatedNode.m in Sources */, - 13E501D41D07A644005F35D8 /* RCTViewPropertyMapper.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m index 53e67eb7745b4f..55763bfe57dc28 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m @@ -121,8 +121,9 @@ - (void)setBridge:(RCTBridge *)bridge RCT_EXPORT_METHOD(connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag viewTag:(nonnull NSNumber *)viewTag) { + NSString *viewName = [self.bridge.uiManager viewNameForReactTag:viewTag]; [_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) { - [nodesManager connectAnimatedNodeToView:nodeTag viewTag:viewTag]; + [nodesManager connectAnimatedNodeToView:nodeTag viewTag:viewTag viewName:viewName]; }]; } diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index 1330aad49de040..e911f1b37c2dd5 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -32,7 +32,8 @@ childTag:(nonnull NSNumber *)childTag; - (void)connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag - viewTag:(nonnull NSNumber *)viewTag; + viewTag:(nonnull NSNumber *)viewTag + viewName:(nonnull NSString *)viewName; - (void)disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag viewTag:(nonnull NSNumber *)viewTag; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m index c1dc22fabf5e0f..ac15ad705ddd3c 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m @@ -116,10 +116,11 @@ - (void)disconnectAnimatedNodes:(nonnull NSNumber *)parentTag - (void)connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag viewTag:(nonnull NSNumber *)viewTag + viewName:(nonnull NSString *)viewName { RCTAnimatedNode *node = _animationNodes[nodeTag]; - if (viewTag && [node isKindOfClass:[RCTPropsAnimatedNode class]]) { - [(RCTPropsAnimatedNode *)node connectToView:viewTag uiManager:_uiManager]; + if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) { + [(RCTPropsAnimatedNode *)node connectToView:viewTag viewName:viewName uiManager:_uiManager]; } [node setNeedsUpdate]; } @@ -128,7 +129,7 @@ - (void)disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag viewTag:(nonnull NSNumber *)viewTag { RCTAnimatedNode *node = _animationNodes[nodeTag]; - if (viewTag && node && [node isKindOfClass:[RCTPropsAnimatedNode class]]) { + if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) { [(RCTPropsAnimatedNode *)node disconnectFromView:viewTag]; } } @@ -294,7 +295,7 @@ - (void)startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag valueObserver:(id)valueObserver { RCTAnimatedNode *node = _animationNodes[tag]; - if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) { + if ([node isKindOfClass:[RCTValueAnimatedNode class]]) { ((RCTValueAnimatedNode *)node).valueObserver = valueObserver; } } @@ -303,7 +304,7 @@ - (void)stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag valueObserver:(id)valueObserver { RCTAnimatedNode *node = _animationNodes[tag]; - if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) { + if ([node isKindOfClass:[RCTValueAnimatedNode class]]) { ((RCTValueAnimatedNode *)node).valueObserver = valueObserver; } } diff --git a/Libraries/NativeAnimation/RCTViewPropertyMapper.h b/Libraries/NativeAnimation/RCTViewPropertyMapper.h deleted file mode 100644 index 6f2a98f116ed5a..00000000000000 --- a/Libraries/NativeAnimation/RCTViewPropertyMapper.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -@class RCTUIManager; - -@interface RCTViewPropertyMapper : NSObject - -@property (nonatomic, readonly) NSNumber *viewTag; - -- (instancetype)initWithViewTag:(NSNumber *)viewTag - uiManager:(RCTUIManager *)uiManager NS_DESIGNATED_INITIALIZER; - -- (void)updateViewWithDictionary:(NSDictionary *)updates; - -@end diff --git a/Libraries/NativeAnimation/RCTViewPropertyMapper.m b/Libraries/NativeAnimation/RCTViewPropertyMapper.m deleted file mode 100644 index 40c8b5625935cb..00000000000000 --- a/Libraries/NativeAnimation/RCTViewPropertyMapper.m +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "RCTViewPropertyMapper.h" - -#import - -#import -#import -#import - -#import "RCTNativeAnimatedModule.h" - -@interface RCTViewPropertyMapper () - -@property (nonatomic, weak) UIView *cachedView; -@property (nonatomic, weak) RCTUIManager *uiManager; - -@end - -@implementation RCTViewPropertyMapper - -- (instancetype)initWithViewTag:(NSNumber *)viewTag - uiManager:(RCTUIManager *)uiManager -{ - if ((self = [super init])) { - _uiManager = uiManager; - _viewTag = viewTag; - } - return self; -} - -RCT_NOT_IMPLEMENTED(- (instancetype)init) - -- (void)updateViewWithDictionary:(NSDictionary *)properties -{ - // cache the view for perf reasons (avoid constant lookups) - UIView *view = _cachedView = _cachedView ?: [self.uiManager viewForReactTag:_viewTag]; - if (!view) { - RCTLogError(@"No view to update."); - return; - } - - if (!properties.count) { - return; - } - - NSNumber *opacity = [RCTConvert NSNumber:properties[@"opacity"]]; - if (opacity) { - view.alpha = opacity.floatValue; - } - - NSObject *transform = properties[@"transform"]; - if ([transform isKindOfClass:[NSValue class]]) { - view.layer.allowsEdgeAntialiasing = YES; - view.layer.transform = ((NSValue *)transform).CATransform3DValue; - } -} - -@end diff --git a/Libraries/StyleSheet/processTransform.js b/Libraries/StyleSheet/processTransform.js index bddd54f54548c9..a00dc1d0196b9b 100644 --- a/Libraries/StyleSheet/processTransform.js +++ b/Libraries/StyleSheet/processTransform.js @@ -30,10 +30,10 @@ function processTransform(transform: Object): Object { _validateTransforms(transform); } - // Android implementation of transform property accepts the list of transform - // properties as opposed to a transform Matrix. This is necessary to control - // transform property updates completely on the native thread. - if (Platform.OS === 'android') { + // Android & iOS implementations of transform property accept the list of + // transform properties as opposed to a transform Matrix. This is necessary + // to control transform property updates completely on the native thread. + if (Platform.OS === 'android' || Platform.OS === 'ios') { return transform; } diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index a71cc7857e3f10..520c216b54cb30 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -85,7 +85,6 @@ typedef NSURL RCTFileURL; + (CGLineCap)CGLineCap:(id)json; + (CGLineJoin)CGLineJoin:(id)json; -+ (CATransform3D)CATransform3D:(id)json; + (CGAffineTransform)CGAffineTransform:(id)json; + (UIColor *)UIColor:(id)json; diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 084caec3494cde..1a33d84b676d84 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -465,13 +465,6 @@ + (type)type:(id)json \ @"square": @(kCGLineCapSquare), }), kCGLineCapButt, intValue) -RCT_CGSTRUCT_CONVERTER(CATransform3D, (@[ - @"m11", @"m12", @"m13", @"m14", - @"m21", @"m22", @"m23", @"m24", - @"m31", @"m32", @"m33", @"m34", - @"m41", @"m42", @"m43", @"m44" -]), nil) - RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[ @"a", @"b", @"c", @"d", @"tx", @"ty" ]), nil) diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index e31ca39ec8ce1c..421bea883c5490 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -60,6 +60,11 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey; */ - (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility; +/** + * Gets the view name associated with a reactTag. + */ +- (NSString *)viewNameForReactTag:(NSNumber *)reactTag; + /** * Gets the view associated with a reactTag. */ @@ -90,6 +95,16 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey; */ - (void)addUIBlock:(RCTViewManagerUIBlock)block; +/** + * Used by native animated module to bypass the process of updating the values through the shadow + * view hierarchy. This method will directly update native views, which means that updates for + * layout-related propertied won't be handled properly. + * Make sure you know what you're doing before calling this method :) + */ +- (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag + viewName:(NSString *)viewName + props:(NSDictionary *)props; + /** * Given a reactTag from a component, find its root view, if possible. * Otherwise, this will give back nil. diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index f0a3bfaac516d1..3777540492caab 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -413,6 +413,12 @@ - (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSize userInfo:@{RCTUIManagerRootViewKey: rootView}]; } +- (NSString *)viewNameForReactTag:(NSNumber *)reactTag +{ + RCTAssertThread(RCTGetUIManagerQueue(), @"viewNameForReactTag can only be called from the shadow queue"); + return _shadowViewRegistry[reactTag].viewName; +} + - (UIView *)viewForReactTag:(NSNumber *)reactTag { RCTAssertMainQueue(); @@ -1053,6 +1059,16 @@ - (void)_manageChildren:(NSNumber *)containerTag }]; } +- (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag + viewName:(NSString *)viewName + props:(NSDictionary *)props +{ + RCTAssertMainQueue(); + RCTComponentData *componentData = _componentDataByName[viewName]; + UIView *view = _viewRegistry[reactTag]; + [componentData setProps:props forView:view]; +} + RCT_EXPORT_METHOD(focus:(nonnull NSNumber *)reactTag) { [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index e335b88d24200e..0cb26160beacef 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -721,6 +721,9 @@ A2440AA31DF8D854006E7BFC /* RCTReloadCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A2440AA11DF8D854006E7BFC /* RCTReloadCommand.m */; }; A2440AA41DF8D865006E7BFC /* RCTReloadCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = A2440AA01DF8D854006E7BFC /* RCTReloadCommand.h */; }; AC70D2E91DE489E4002E6351 /* RCTJavaScriptLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */; }; + AC70D2ED1DE48A22002E6351 /* JSBundleType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */; }; + 945929C41DD62ADD00653A7D /* RCTConvert+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 945929C31DD62ADD00653A7D /* RCTConvert+Transform.m */; }; + 945929C51DD62ADD00653A7D /* RCTConvert+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 945929C31DD62ADD00653A7D /* RCTConvert+Transform.m */; }; B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */; }; B95154321D1B34B200FE7B80 /* RCTActivityIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */; }; E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; }; @@ -1356,6 +1359,8 @@ AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTJavaScriptLoader.mm; sourceTree = ""; }; AC70D2EB1DE48A22002E6351 /* JSBundleType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBundleType.cpp; sourceTree = ""; }; AC70D2EE1DE48AC5002E6351 /* oss-compat-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "oss-compat-util.h"; sourceTree = ""; }; + 945929C21DD62ADD00653A7D /* RCTConvert+Transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+Transform.h"; sourceTree = ""; }; + 945929C31DD62ADD00653A7D /* RCTConvert+Transform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Transform.m"; sourceTree = ""; }; ACDD3FDA1BC7430D00E7DE33 /* RCTBorderStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderStyle.h; sourceTree = ""; }; B233E6E81D2D843200BC68BA /* RCTI18nManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTI18nManager.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTI18nManager.m; sourceTree = ""; }; @@ -1479,6 +1484,8 @@ 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */, 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */, 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */, + 945929C21DD62ADD00653A7D /* RCTConvert+Transform.h */, + 945929C31DD62ADD00653A7D /* RCTConvert+Transform.m */, 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */, 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */, 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */, @@ -2417,6 +2424,7 @@ 2D3B5E941D9B087900451313 /* RCTBundleURLProvider.m in Sources */, 2D3B5EB81D9B091B00451313 /* RCTSourceCode.m in Sources */, 2D3B5EB51D9B091100451313 /* RCTDevMenu.mm in Sources */, + 945929C51DD62ADD00653A7D /* RCTConvert+Transform.m in Sources */, 2D3B5EBD1D9B092A00451313 /* RCTTiming.m in Sources */, 2D3B5EA81D9B08D300451313 /* RCTUtils.m in Sources */, 2D3B5EC81D9B095800451313 /* RCTActivityIndicatorViewManager.m in Sources */, @@ -2605,6 +2613,7 @@ 13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */, 391E86A41C623EC800009732 /* RCTTouchEvent.m in Sources */, 1450FF861BCFF28A00208362 /* RCTProfile.m in Sources */, + 945929C41DD62ADD00653A7D /* RCTConvert+Transform.m in Sources */, 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */, 13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */, ); diff --git a/React/Views/RCTConvert+Transform.h b/React/Views/RCTConvert+Transform.h new file mode 100644 index 00000000000000..29c65823bcb26c --- /dev/null +++ b/React/Views/RCTConvert+Transform.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTConvert.h" + +@interface RCTConvert (Transform) + ++ (CATransform3D)CATransform3D:(id)json; + +@end diff --git a/React/Views/RCTConvert+Transform.m b/React/Views/RCTConvert+Transform.m new file mode 100644 index 00000000000000..1286553c5af87d --- /dev/null +++ b/React/Views/RCTConvert+Transform.m @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTConvert+Transform.h" + +static const NSUInteger kMatrixArrayLength = 4 * 4; + +@implementation RCTConvert (Transform) + ++ (CGFloat)convertToRadians:(id)json +{ + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + if ([stringValue hasSuffix:@"deg"]) { + CGFloat degrees = [[stringValue substringToIndex:stringValue.length - 3] floatValue]; + return degrees * M_PI / 180; + } + if ([stringValue hasSuffix:@"rad"]) { + return [[stringValue substringToIndex:stringValue.length - 3] floatValue]; + } + } + return [json floatValue]; +} + ++ (CATransform3D)CATransform3DFromMatrix:(id)json +{ + CATransform3D transform = CATransform3DIdentity; + if (!json) { + return transform; + } + if (![json isKindOfClass:[NSArray class]]) { + RCTLogConvertError(json, @"a CATransform3D. Expected array for transform matrix."); + return transform; + } + if ([json count] != kMatrixArrayLength) { + RCTLogConvertError(json, @"a CATransform3D. Expected 4x4 matrix array."); + return transform; + } + for (NSUInteger i = 0; i < kMatrixArrayLength; i++) { + ((CGFloat *)&transform)[i] = [RCTConvert CGFloat:json[i]]; + } + return transform; +} + ++ (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]; + } + 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"]) { + transform = [self CATransform3DFromMatrix:value]; + + } else if ([property isEqualToString:@"perspective"]) { + transform.m34 = -1 / [value floatValue]; + + } 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]; + transform = CATransform3DScale(transform, scale, scale, 1); + + } else if ([property isEqualToString:@"scaleX"]) { + CGFloat scale = [value floatValue]; + transform = CATransform3DScale(transform, scale, 1, 1); + + } else if ([property isEqualToString:@"scaleY"]) { + CGFloat scale = [value floatValue]; + 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]; + transform.m21 = sinf(skew); + transform.m22 = cosf(skew); + + } else if ([property isEqualToString:@"skewY"]) { + CGFloat skew = [self convertToRadians:value]; + transform.m11 = cosf(skew); + transform.m12 = sinf(skew); + + } else { + RCTLogError(@"Unsupported transform type for a CATransform3D: %@.", property); + } + } + return transform; +} + +@end diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index cc1ac26b16d34e..cbecf8c13abefb 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -19,6 +19,7 @@ #import "RCTUtils.h" #import "RCTView.h" #import "UIView+React.h" +#import "RCTConvert+Transform.h" #if TARGET_OS_TV #import "RCTTVView.h"