RCTAnimation & RCTLayoutAnimation were decoupled from RCTUIManager

Reviewed By: javache

Differential Revision: D5351726

fbshipit-source-id: f13e5cd47483f2d5f9b194c10ae3fb6e99e08d84
This commit is contained in:
Valentin Shergin
2017-07-03 16:13:34 -07:00
committed by Facebook Github Bot
parent e7c1cf5b7d
commit 6312d67bcb
7 changed files with 381 additions and 187 deletions

View File

@@ -0,0 +1,38 @@
/**
* 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 <UIKit/UIKit.h>
#import <React/RCTAnimationType.h>
@interface RCTLayoutAnimation : NSObject
@property (nonatomic, readonly) NSTimeInterval duration;
@property (nonatomic, readonly) NSTimeInterval delay;
@property (nonatomic, readonly, copy) NSString *property;
@property (nonatomic, readonly) CGFloat springDamping;
@property (nonatomic, readonly) CGFloat initialVelocity;
@property (nonatomic, readonly) RCTAnimationType animationType;
+ (void)initializeStatics;
- (instancetype)initWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
property:(NSString *)property
springDamping:(CGFloat)springDamping
initialVelocity:(CGFloat)initialVelocity
animationType:(RCTAnimationType)animationType;
- (instancetype)initWithDuration:(NSTimeInterval)duration
config:(NSDictionary *)config;
- (void)performAnimations:(void (^)(void))animations
withCompletionBlock:(void (^)(BOOL completed))completionBlock;
@end

View File

@@ -0,0 +1,155 @@
/**
* 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 "RCTLayoutAnimation.h"
#import "RCTConvert.h"
@implementation RCTLayoutAnimation
static UIViewAnimationCurve _currentKeyboardAnimationCurve;
static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnimationType type)
{
switch (type) {
case RCTAnimationTypeLinear:
return UIViewAnimationOptionCurveLinear;
case RCTAnimationTypeEaseIn:
return UIViewAnimationOptionCurveEaseIn;
case RCTAnimationTypeEaseOut:
return UIViewAnimationOptionCurveEaseOut;
case RCTAnimationTypeEaseInEaseOut:
return UIViewAnimationOptionCurveEaseInOut;
case RCTAnimationTypeKeyboard:
// http://stackoverflow.com/questions/18870447/how-to-use-the-default-ios7-uianimation-curve
return (UIViewAnimationOptions)(_currentKeyboardAnimationCurve << 16);
default:
RCTLogError(@"Unsupported animation type %zd", type);
return UIViewAnimationOptionCurveEaseInOut;
}
}
// Use a custom initialization function rather than implementing `+initialize` so that we can control
// when the initialization code runs. `+initialize` runs immediately before the first message is sent
// to the class which may be too late for us. By this time, we may have missed some
// `UIKeyboardWillChangeFrameNotification`s.
+ (void)initializeStatics
{
#if !TARGET_OS_TV
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillChangeFrame:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
});
#endif
}
+ (void)keyboardWillChangeFrame:(NSNotification *)notification
{
#if !TARGET_OS_TV
NSDictionary *userInfo = notification.userInfo;
_currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
#endif
}
- (instancetype)initWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
property:(NSString *)property
springDamping:(CGFloat)springDamping
initialVelocity:(CGFloat)initialVelocity
animationType:(RCTAnimationType)animationType
{
if (self = [super init]) {
_duration = duration;
_delay = delay;
_property = property;
_springDamping = springDamping;
_initialVelocity = initialVelocity;
_animationType = animationType;
}
return self;
}
- (instancetype)initWithDuration:(NSTimeInterval)duration
config:(NSDictionary *)config
{
if (!config) {
return nil;
}
if (self = [super init]) {
_property = [RCTConvert NSString:config[@"property"]];
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] ?: duration;
if (_duration > 0.0 && _duration < 0.01) {
RCTLogError(@"RCTLayoutAnimationGroup expects timings to be in ms, not seconds.");
_duration = _duration * 1000.0;
}
_delay = [RCTConvert NSTimeInterval:config[@"delay"]];
if (_delay > 0.0 && _delay < 0.01) {
RCTLogError(@"RCTLayoutAnimationGroup expects timings to be in ms, not seconds.");
_delay = _delay * 1000.0;
}
_animationType = [RCTConvert RCTAnimationType:config[@"type"]];
if (_animationType == RCTAnimationTypeSpring) {
_springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
_initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
}
}
return self;
}
- (void)performAnimations:(void (^)(void))animations
withCompletionBlock:(void (^)(BOOL completed))completionBlock
{
if (_animationType == RCTAnimationTypeSpring) {
[UIView animateWithDuration:_duration
delay:_delay
usingSpringWithDamping:_springDamping
initialSpringVelocity:_initialVelocity
options:UIViewAnimationOptionBeginFromCurrentState
animations:animations
completion:completionBlock];
} else {
UIViewAnimationOptions options =
UIViewAnimationOptionBeginFromCurrentState |
UIViewAnimationOptionsFromRCTAnimationType(_animationType);
[UIView animateWithDuration:_duration
delay:_delay
options:options
animations:animations
completion:completionBlock];
}
}
- (BOOL)isEqual:(RCTLayoutAnimation *)animation
{
return
_duration == animation.duration &&
_delay == animation.delay &&
(_property == animation.property || [_property isEqualToString:animation.property]) &&
_springDamping == animation.springDamping &&
_initialVelocity == animation.initialVelocity &&
_animationType == animation.animationType;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; duration: %f; delay: %f; property: %@; springDamping: %f; initialVelocity: %f; animationType: %li;>",
NSStringFromClass([self class]), self, _duration, _delay, _property, _springDamping, _initialVelocity, (long)_animationType];
}
@end

View File

@@ -0,0 +1,32 @@
/**
* 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 <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
@class RCTLayoutAnimation;
@interface RCTLayoutAnimationGroup : NSObject
@property (nonatomic, readonly) RCTLayoutAnimation *creatingLayoutAnimation;
@property (nonatomic, readonly) RCTLayoutAnimation *updatingLayoutAnimation;
@property (nonatomic, readonly) RCTLayoutAnimation *deletingLayoutAnimation;
@property (nonatomic, copy) RCTResponseSenderBlock callback;
- (instancetype)initWithCreatingLayoutAnimation:(RCTLayoutAnimation *)creatingLayoutAnimation
updatingLayoutAnimation:(RCTLayoutAnimation *)updatingLayoutAnimation
deletingLayoutAnimation:(RCTLayoutAnimation *)deletingLayoutAnimation
callback:(RCTResponseSenderBlock)callback;
- (instancetype)initWithConfig:(NSDictionary *)config
callback:(RCTResponseSenderBlock)callback;
@end

View File

@@ -0,0 +1,74 @@
/**
* 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 "RCTLayoutAnimationGroup.h"
#import "RCTLayoutAnimation.h"
#import "RCTConvert.h"
@implementation RCTLayoutAnimationGroup
- (instancetype)initWithCreatingLayoutAnimation:(RCTLayoutAnimation *)creatingLayoutAnimation
updatingLayoutAnimation:(RCTLayoutAnimation *)updatingLayoutAnimation
deletingLayoutAnimation:(RCTLayoutAnimation *)deletingLayoutAnimation
callback:(RCTResponseSenderBlock)callback
{
if (self = [super init]) {
_creatingLayoutAnimation = creatingLayoutAnimation;
_updatingLayoutAnimation = updatingLayoutAnimation;
_deletingLayoutAnimation = deletingLayoutAnimation;
_callback = callback;
}
return self;
}
- (instancetype)initWithConfig:(NSDictionary *)config
callback:(RCTResponseSenderBlock)callback
{
if (!config) {
return nil;
}
if (self = [super init]) {
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]];
if (duration > 0.0 && duration < 0.01) {
RCTLogError(@"RCTLayoutAnimationGroup expects timings to be in ms, not seconds.");
duration = duration * 1000.0;
}
_creatingLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDuration:duration config:config[@"create"]];
_updatingLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDuration:duration config:config[@"update"]];
_deletingLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDuration:duration config:config[@"delete"]];
_callback = callback;
}
return self;
}
- (BOOL)isEqual:(RCTLayoutAnimationGroup *)layoutAnimation
{
RCTLayoutAnimation *creatingLayoutAnimation = layoutAnimation.creatingLayoutAnimation;
RCTLayoutAnimation *updatingLayoutAnimation = layoutAnimation.updatingLayoutAnimation;
RCTLayoutAnimation *deletingLayoutAnimation = layoutAnimation.deletingLayoutAnimation;
return
(_creatingLayoutAnimation == creatingLayoutAnimation || [_creatingLayoutAnimation isEqual:creatingLayoutAnimation]) &&
(_updatingLayoutAnimation == updatingLayoutAnimation || [_updatingLayoutAnimation isEqual:updatingLayoutAnimation]) &&
(_deletingLayoutAnimation == deletingLayoutAnimation || [_deletingLayoutAnimation isEqual:deletingLayoutAnimation]);
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; creatingLayoutAnimation: %@; updatingLayoutAnimation: %@; deletingLayoutAnimation: %@>",
NSStringFromClass([self class]), self, [_creatingLayoutAnimation description], [_updatingLayoutAnimation description], [_deletingLayoutAnimation description]];
}
@end

View File

@@ -59,6 +59,7 @@ RCT_EXTERN NSString *const RCTUIManagerDidRemoveRootViewNotification;
*/
RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
@class RCTLayoutAnimationGroup;
@class RCTUIManagerObserverCoordinator;
/**
@@ -115,6 +116,13 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
*/
- (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view;
/**
* Sets up layout animation which will perform on next layout pass.
* The animation will affect only one next layout pass.
* Must be called on the main queue.
*/
- (void)setNextLayoutAnimationGroup:(RCTLayoutAnimationGroup *)layoutAnimationGroup;
/**
* Schedule a block to be executed on the UI thread. Useful if you need to execute
* view logic after all currently queued view updates have completed.

View File

@@ -23,6 +23,8 @@
#import "RCTConvert.h"
#import "RCTDefines.h"
#import "RCTEventDispatcher.h"
#import "RCTLayoutAnimation.h"
#import "RCTLayoutAnimationGroup.h"
#import "RCTLog.h"
#import "RCTModuleData.h"
#import "RCTModuleMethod.h"
@@ -55,160 +57,6 @@ NSString *const RCTUIManagerDidRegisterRootViewNotification = @"RCTUIManagerDidR
NSString *const RCTUIManagerDidRemoveRootViewNotification = @"RCTUIManagerDidRemoveRootViewNotification";
NSString *const RCTUIManagerRootViewKey = @"RCTUIManagerRootViewKey";
@interface RCTAnimation : NSObject
@property (nonatomic, readonly) NSTimeInterval duration;
@property (nonatomic, readonly) NSTimeInterval delay;
@property (nonatomic, readonly, copy) NSString *property;
@property (nonatomic, readonly) CGFloat springDamping;
@property (nonatomic, readonly) CGFloat initialVelocity;
@property (nonatomic, readonly) RCTAnimationType animationType;
@end
static UIViewAnimationCurve _currentKeyboardAnimationCurve;
@implementation RCTAnimation
static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnimationType type)
{
switch (type) {
case RCTAnimationTypeLinear:
return UIViewAnimationOptionCurveLinear;
case RCTAnimationTypeEaseIn:
return UIViewAnimationOptionCurveEaseIn;
case RCTAnimationTypeEaseOut:
return UIViewAnimationOptionCurveEaseOut;
case RCTAnimationTypeEaseInEaseOut:
return UIViewAnimationOptionCurveEaseInOut;
case RCTAnimationTypeKeyboard:
// http://stackoverflow.com/questions/18870447/how-to-use-the-default-ios7-uianimation-curve
return (UIViewAnimationOptions)(_currentKeyboardAnimationCurve << 16);
default:
RCTLogError(@"Unsupported animation type %zd", type);
return UIViewAnimationOptionCurveEaseInOut;
}
}
// Use a custom initialization function rather than implementing `+initialize` so that we can control
// when the initialization code runs. `+initialize` runs immediately before the first message is sent
// to the class which may be too late for us. By this time, we may have missed some
// `UIKeyboardWillChangeFrameNotification`s.
+ (void)initializeStatics
{
#if !TARGET_OS_TV
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillChangeFrame:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
});
#endif
}
+ (void)keyboardWillChangeFrame:(NSNotification *)notification
{
#if !TARGET_OS_TV
NSDictionary *userInfo = notification.userInfo;
_currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
#endif
}
- (instancetype)initWithDuration:(NSTimeInterval)duration dictionary:(NSDictionary *)config
{
if (!config) {
return nil;
}
if ((self = [super init])) {
_property = [RCTConvert NSString:config[@"property"]];
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] ?: duration;
if (_duration > 0.0 && _duration < 0.01) {
RCTLogError(@"RCTLayoutAnimation expects timings to be in ms, not seconds.");
_duration = _duration * 1000.0;
}
_delay = [RCTConvert NSTimeInterval:config[@"delay"]];
if (_delay > 0.0 && _delay < 0.01) {
RCTLogError(@"RCTLayoutAnimation expects timings to be in ms, not seconds.");
_delay = _delay * 1000.0;
}
_animationType = [RCTConvert RCTAnimationType:config[@"type"]];
if (_animationType == RCTAnimationTypeSpring) {
_springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
_initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
}
}
return self;
}
- (void)performAnimations:(void (^)(void))animations
withCompletionBlock:(void (^)(BOOL completed))completionBlock
{
if (_animationType == RCTAnimationTypeSpring) {
[UIView animateWithDuration:_duration
delay:_delay
usingSpringWithDamping:_springDamping
initialSpringVelocity:_initialVelocity
options:UIViewAnimationOptionBeginFromCurrentState
animations:animations
completion:completionBlock];
} else {
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState |
UIViewAnimationOptionsFromRCTAnimationType(_animationType);
[UIView animateWithDuration:_duration
delay:_delay
options:options
animations:animations
completion:completionBlock];
}
}
@end
@interface RCTLayoutAnimation : NSObject
@property (nonatomic, copy) NSDictionary *config;
@property (nonatomic, strong) RCTAnimation *createAnimation;
@property (nonatomic, strong) RCTAnimation *updateAnimation;
@property (nonatomic, strong) RCTAnimation *deleteAnimation;
@property (nonatomic, copy) RCTResponseSenderBlock callback;
@end
@implementation RCTLayoutAnimation
- (instancetype)initWithDictionary:(NSDictionary *)config callback:(RCTResponseSenderBlock)callback
{
if (!config) {
return nil;
}
if ((self = [super init])) {
_config = [config copy];
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]];
if (duration > 0.0 && duration < 0.01) {
RCTLogError(@"RCTLayoutAnimation expects timings to be in ms, not seconds.");
duration = duration * 1000.0;
}
_createAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"create"]];
_updateAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"update"]];
_deleteAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"delete"]];
_callback = callback;
}
return self;
}
@end
@implementation RCTUIManager
{
// Root views are only mutated on the shadow queue
@@ -216,7 +64,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
NSMutableArray<RCTViewManagerUIBlock> *_pendingUIBlocks;
// Animation
RCTLayoutAnimation *_layoutAnimation; // Main thread only
RCTLayoutAnimationGroup *_layoutAnimationGroup; // Main thread only
NSMutableSet<UIView *> *_viewsToBeDeleted; // Main thread only
NSMutableDictionary<NSNumber *, RCTShadowView *> *_shadowViewRegistry; // RCT thread only
@@ -326,7 +174,7 @@ RCT_EXPORT_MODULE()
selector:@selector(didReceiveNewContentSizeMultiplier)
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
object:_bridge.accessibilityManager];
[RCTAnimation initializeStatics];
[RCTLayoutAnimation initializeStatics];
}
dispatch_queue_t RCTGetUIManagerQueue(void)
@@ -559,6 +407,19 @@ BOOL RCTIsUIManagerQueue()
[_pendingUIBlocks insertObject:block atIndex:0];
}
- (void)setNextLayoutAnimationGroup:(RCTLayoutAnimationGroup *)layoutAnimationGroup
{
RCTAssertMainQueue();
if (_layoutAnimationGroup && ![_layoutAnimationGroup isEqual:layoutAnimationGroup]) {
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.",
[_layoutAnimationGroup description],
[layoutAnimationGroup description]);
}
_layoutAnimationGroup = layoutAnimationGroup;
}
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
{
RCTAssert(!RCTIsMainQueue(), @"Should be called on shadow queue");
@@ -649,7 +510,7 @@ BOOL RCTIsUIManagerQueue()
return ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
const RCTFrameData *frameDataArray = (const RCTFrameData *)framesData.bytes;
RCTLayoutAnimation *layoutAnimation = uiManager->_layoutAnimation;
RCTLayoutAnimationGroup *layoutAnimationGroup = uiManager->_layoutAnimationGroup;
__block NSUInteger completionsCalled = 0;
@@ -663,18 +524,18 @@ BOOL RCTIsUIManagerQueue()
BOOL isHidden = frameData.isHidden;
UIUserInterfaceLayoutDirection layoutDirection = frameData.layoutDirection;
BOOL isNew = frameData.isNew;
RCTAnimation *updateAnimation = isNew ? nil : layoutAnimation.updateAnimation;
RCTLayoutAnimation *updatingLayoutAnimation = isNew ? nil : layoutAnimationGroup.updatingLayoutAnimation;
BOOL shouldAnimateCreation = isNew && !frameData.parentIsNew;
RCTAnimation *createAnimation = shouldAnimateCreation ? layoutAnimation.createAnimation : nil;
RCTLayoutAnimation *creatingLayoutAnimation = shouldAnimateCreation ? layoutAnimationGroup.creatingLayoutAnimation : nil;
void (^completion)(BOOL) = ^(BOOL finished) {
completionsCalled++;
if (layoutAnimation.callback && completionsCalled == count) {
layoutAnimation.callback(@[@(finished)]);
if (layoutAnimationGroup.callback && completionsCalled == count) {
layoutAnimationGroup.callback(@[@(finished)]);
// It's unsafe to call this callback more than once, so we nil it out here
// to make sure that doesn't happen.
layoutAnimation.callback = nil;
layoutAnimationGroup.callback = nil;
}
};
@@ -687,7 +548,7 @@ BOOL RCTIsUIManagerQueue()
}
RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag];
if (createAnimation) {
if (creatingLayoutAnimation) {
// Animate view creation
[view reactSetFrame:frame];
@@ -695,17 +556,17 @@ BOOL RCTIsUIManagerQueue()
CATransform3D finalTransform = view.layer.transform;
CGFloat finalOpacity = view.layer.opacity;
NSString *property = createAnimation.property;
NSString *property = creatingLayoutAnimation.property;
if ([property isEqualToString:@"scaleXY"]) {
view.layer.transform = CATransform3DMakeScale(0, 0, 0);
} else if ([property isEqualToString:@"opacity"]) {
view.layer.opacity = 0.0;
} else {
RCTLogError(@"Unsupported layout animation createConfig property %@",
createAnimation.property);
creatingLayoutAnimation.property);
}
[createAnimation performAnimations:^{
[creatingLayoutAnimation performAnimations:^{
if ([property isEqualToString:@"scaleXY"]) {
view.layer.transform = finalTransform;
} else if ([property isEqualToString:@"opacity"]) {
@@ -716,10 +577,10 @@ BOOL RCTIsUIManagerQueue()
}
} withCompletionBlock:completion];
} else if (updateAnimation) {
} else if (updatingLayoutAnimation) {
// Animate view update
[updateAnimation performAnimations:^{
[updatingLayoutAnimation performAnimations:^{
[view reactSetFrame:frame];
if (updateBlock) {
updateBlock(self, viewRegistry);
@@ -738,7 +599,7 @@ BOOL RCTIsUIManagerQueue()
}
// Clean up
uiManager->_layoutAnimation = nil;
uiManager->_layoutAnimationGroup = nil;
};
}
@@ -823,10 +684,10 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
*/
- (void)_removeChildren:(NSArray<UIView *> *)children
fromContainer:(UIView *)container
withAnimation:(RCTLayoutAnimation *)animation
withAnimation:(RCTLayoutAnimationGroup *)animation
{
RCTAssertMainQueue();
RCTAnimation *deleteAnimation = animation.deleteAnimation;
RCTLayoutAnimation *deletingLayoutAnimation = animation.deletingLayoutAnimation;
__block NSUInteger completionsCalled = 0;
for (UIView *removedChild in children) {
@@ -852,15 +713,15 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
// the view events anyway.
removedChild.userInteractionEnabled = NO;
NSString *property = deleteAnimation.property;
[deleteAnimation performAnimations:^{
NSString *property = deletingLayoutAnimation.property;
[deletingLayoutAnimation performAnimations:^{
if ([property isEqualToString:@"scaleXY"]) {
removedChild.layer.transform = CATransform3DMakeScale(0.001, 0.001, 0.001);
} else if ([property isEqualToString:@"opacity"]) {
removedChild.layer.opacity = 0.0;
} else {
RCTLogError(@"Unsupported layout animation createConfig property %@",
deleteAnimation.property);
deletingLayoutAnimation.property);
}
} withCompletionBlock:completion];
}
@@ -985,10 +846,10 @@ RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerTag
[self _childrenToRemoveFromContainer:container atIndices:moveFromIndices];
BOOL isUIViewRegistry = ((id)registry == (id)_viewRegistry);
if (isUIViewRegistry && _layoutAnimation.deleteAnimation) {
if (isUIViewRegistry && _layoutAnimationGroup.deletingLayoutAnimation) {
[self _removeChildren:(NSArray<UIView *> *)permanentlyRemovedChildren
fromContainer:(UIView *)container
withAnimation:_layoutAnimation];
withAnimation:_layoutAnimationGroup];
} else {
[self _removeChildren:permanentlyRemovedChildren fromContainer:container];
}
@@ -1587,18 +1448,12 @@ RCT_EXPORT_METHOD(configureNextLayoutAnimation:(NSDictionary *)config
withCallback:(RCTResponseSenderBlock)callback
errorCallback:(__unused RCTResponseSenderBlock)errorCallback)
{
RCTLayoutAnimation *currentAnimation = _layoutAnimation;
RCTLayoutAnimationGroup *layoutAnimationGroup =
[[RCTLayoutAnimationGroup alloc] initWithConfig:config
callback:callback];
if (currentAnimation && ![config isEqualToDictionary:currentAnimation.config]) {
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.", currentAnimation.config, config);
}
RCTLayoutAnimation *nextLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDictionary:config
callback:callback];
// Set up next layout animation
[self addUIBlock:^(RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
uiManager->_layoutAnimation = nextLayoutAnimation;
[uiManager setNextLayoutAnimationGroup:layoutAnimationGroup];
}];
}