Skip to content

Commit

Permalink
RCTAnimation & RCTLayoutAnimation were decoupled from RCTUIManager
Browse files Browse the repository at this point in the history
Reviewed By: javache

Differential Revision: D5351726

fbshipit-source-id: f13e5cd47483f2d5f9b194c10ae3fb6e99e08d84
  • Loading branch information
shergin authored and facebook-github-bot committed Jul 3, 2017
1 parent e7c1cf5 commit 6312d67
Show file tree
Hide file tree
Showing 7 changed files with 381 additions and 187 deletions.
38 changes: 38 additions & 0 deletions React/Modules/RCTLayoutAnimation.h
Original file line number Diff line number Diff line change
@@ -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
155 changes: 155 additions & 0 deletions React/Modules/RCTLayoutAnimation.m
Original file line number Diff line number Diff line change
@@ -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
32 changes: 32 additions & 0 deletions React/Modules/RCTLayoutAnimationGroup.h
Original file line number Diff line number Diff line change
@@ -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
74 changes: 74 additions & 0 deletions React/Modules/RCTLayoutAnimationGroup.m
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions React/Modules/RCTUIManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ RCT_EXTERN NSString *const RCTUIManagerDidRemoveRootViewNotification;
*/
RCT_EXTERN NSString *const RCTUIManagerRootViewKey;

@class RCTLayoutAnimationGroup;
@class RCTUIManagerObserverCoordinator;

/**
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 6312d67

Please sign in to comment.