RCTLayoutAnimation.m 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTLayoutAnimation.h"
  8. #import "RCTConvert.h"
  9. @implementation RCTLayoutAnimation
  10. static UIViewAnimationCurve _currentKeyboardAnimationCurve;
  11. static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnimationType type)
  12. {
  13. switch (type) {
  14. case RCTAnimationTypeLinear:
  15. return UIViewAnimationOptionCurveLinear;
  16. case RCTAnimationTypeEaseIn:
  17. return UIViewAnimationOptionCurveEaseIn;
  18. case RCTAnimationTypeEaseOut:
  19. return UIViewAnimationOptionCurveEaseOut;
  20. case RCTAnimationTypeEaseInEaseOut:
  21. return UIViewAnimationOptionCurveEaseInOut;
  22. case RCTAnimationTypeKeyboard:
  23. // http://stackoverflow.com/questions/18870447/how-to-use-the-default-ios7-uianimation-curve
  24. return (UIViewAnimationOptions)(_currentKeyboardAnimationCurve << 16);
  25. default:
  26. RCTLogError(@"Unsupported animation type %lld", (long long)type);
  27. return UIViewAnimationOptionCurveEaseInOut;
  28. }
  29. }
  30. // Use a custom initialization function rather than implementing `+initialize` so that we can control
  31. // when the initialization code runs. `+initialize` runs immediately before the first message is sent
  32. // to the class which may be too late for us. By this time, we may have missed some
  33. // `UIKeyboardWillChangeFrameNotification`s.
  34. + (void)initializeStatics
  35. {
  36. #if !TARGET_OS_TV
  37. static dispatch_once_t onceToken;
  38. dispatch_once(&onceToken, ^{
  39. [[NSNotificationCenter defaultCenter] addObserver:self
  40. selector:@selector(keyboardWillChangeFrame:)
  41. name:UIKeyboardWillChangeFrameNotification
  42. object:nil];
  43. });
  44. #endif
  45. }
  46. + (void)keyboardWillChangeFrame:(NSNotification *)notification
  47. {
  48. #if !TARGET_OS_TV
  49. NSDictionary *userInfo = notification.userInfo;
  50. _currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
  51. #endif
  52. }
  53. - (instancetype)initWithDuration:(NSTimeInterval)duration
  54. delay:(NSTimeInterval)delay
  55. property:(NSString *)property
  56. springDamping:(CGFloat)springDamping
  57. initialVelocity:(CGFloat)initialVelocity
  58. animationType:(RCTAnimationType)animationType
  59. {
  60. if (self = [super init]) {
  61. _duration = duration;
  62. _delay = delay;
  63. _property = property;
  64. _springDamping = springDamping;
  65. _initialVelocity = initialVelocity;
  66. _animationType = animationType;
  67. }
  68. return self;
  69. }
  70. - (instancetype)initWithDuration:(NSTimeInterval)duration config:(NSDictionary *)config
  71. {
  72. if (!config) {
  73. return nil;
  74. }
  75. if (self = [super init]) {
  76. _property = [RCTConvert NSString:config[@"property"]];
  77. _duration = [RCTConvert NSTimeInterval:config[@"duration"]] ?: duration;
  78. if (_duration > 0.0 && _duration < 0.01) {
  79. RCTLogError(@"RCTLayoutAnimationGroup expects timings to be in ms, not seconds.");
  80. _duration = _duration * 1000.0;
  81. }
  82. _delay = [RCTConvert NSTimeInterval:config[@"delay"]];
  83. if (_delay > 0.0 && _delay < 0.01) {
  84. RCTLogError(@"RCTLayoutAnimationGroup expects timings to be in ms, not seconds.");
  85. _delay = _delay * 1000.0;
  86. }
  87. _animationType = [RCTConvert RCTAnimationType:config[@"type"]];
  88. if (_animationType == RCTAnimationTypeSpring) {
  89. _springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
  90. _initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
  91. }
  92. }
  93. return self;
  94. }
  95. - (void)performAnimations:(void (^)(void))animations withCompletionBlock:(void (^)(BOOL completed))completionBlock
  96. {
  97. if (_animationType == RCTAnimationTypeSpring) {
  98. [UIView animateWithDuration:_duration
  99. delay:_delay
  100. usingSpringWithDamping:_springDamping
  101. initialSpringVelocity:_initialVelocity
  102. options:UIViewAnimationOptionBeginFromCurrentState
  103. animations:animations
  104. completion:completionBlock];
  105. } else {
  106. UIViewAnimationOptions options =
  107. UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionsFromRCTAnimationType(_animationType);
  108. [UIView animateWithDuration:_duration
  109. delay:_delay
  110. options:options
  111. animations:animations
  112. completion:completionBlock];
  113. }
  114. }
  115. - (BOOL)isEqual:(RCTLayoutAnimation *)animation
  116. {
  117. return _duration == animation.duration && _delay == animation.delay &&
  118. (_property == animation.property || [_property isEqualToString:animation.property]) &&
  119. _springDamping == animation.springDamping && _initialVelocity == animation.initialVelocity &&
  120. _animationType == animation.animationType;
  121. }
  122. - (NSString *)description
  123. {
  124. return [NSString
  125. stringWithFormat:
  126. @"<%@: %p; duration: %f; delay: %f; property: %@; springDamping: %f; initialVelocity: %f; animationType: %li;>",
  127. NSStringFromClass([self class]),
  128. self,
  129. _duration,
  130. _delay,
  131. _property,
  132. _springDamping,
  133. _initialVelocity,
  134. (long)_animationType];
  135. }
  136. @end