react-native-reanimated 3.0.0-rc.4 → 3.0.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp +97 -15
  2. package/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h +32 -8
  3. package/Common/cpp/NativeModules/NativeReanimatedModule.cpp +16 -1
  4. package/Common/cpp/NativeModules/NativeReanimatedModule.h +8 -1
  5. package/Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp +17 -0
  6. package/Common/cpp/NativeModules/NativeReanimatedModuleSpec.h +8 -0
  7. package/Common/cpp/ReanimatedRuntime/ReanimatedRuntime.cpp +2 -1
  8. package/Common/cpp/ReanimatedRuntime/ReanimatedRuntime.h +1 -0
  9. package/Common/cpp/Tools/RuntimeDecorator.cpp +2 -1
  10. package/android/CMakeLists.txt +1 -0
  11. package/android/build.gradle +9 -1
  12. package/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java +16 -25
  13. package/android/src/main/cpp/LayoutAnimations.cpp +42 -50
  14. package/android/src/main/cpp/LayoutAnimations.h +17 -3
  15. package/android/src/main/cpp/NativeProxy.cpp +55 -6
  16. package/android/src/main/java/com/swmansion/reanimated/NodesManager.java +1 -1
  17. package/android/src/main/java/com/swmansion/reanimated/layoutReanimation/AnimationsManager.java +208 -223
  18. package/android/src/main/java/com/swmansion/reanimated/layoutReanimation/LayoutAnimations.java +7 -5
  19. package/android/src/main/java/com/swmansion/reanimated/layoutReanimation/NativeMethodsHolder.java +5 -3
  20. package/android/src/main/java/com/swmansion/reanimated/layoutReanimation/ViewHierarchyObserver.java +1 -1
  21. package/android/src/paper/java/com/swmansion/reanimated/NativeProxy.java +20 -7
  22. package/android/src/reactNativeVersionPatch/nativeHierarchyManager/62/com/swmansion/reanimated/layoutReanimation/ReanimatedNativeHierarchyManager.java +8 -44
  23. package/android/src/reactNativeVersionPatch/nativeHierarchyManager/latest/com/swmansion/reanimated/layoutReanimation/ReanimatedNativeHierarchyManager.java +15 -61
  24. package/ios/LayoutReanimation/REAAnimationsManager.h +18 -8
  25. package/ios/LayoutReanimation/REAAnimationsManager.m +197 -162
  26. package/ios/LayoutReanimation/REAUIManager.mm +63 -70
  27. package/ios/native/NativeProxy.mm +55 -48
  28. package/lib/commonjs/createAnimatedComponent.js +26 -26
  29. package/lib/commonjs/createAnimatedComponent.js.map +1 -1
  30. package/lib/commonjs/reanimated2/NativeReanimated/NativeReanimated.js +4 -0
  31. package/lib/commonjs/reanimated2/NativeReanimated/NativeReanimated.js.map +1 -1
  32. package/lib/commonjs/reanimated2/core.js +9 -9
  33. package/lib/commonjs/reanimated2/core.js.map +1 -1
  34. package/lib/commonjs/reanimated2/globals.d.js.map +1 -1
  35. package/lib/commonjs/reanimated2/jestUtils.js +17 -9
  36. package/lib/commonjs/reanimated2/jestUtils.js.map +1 -1
  37. package/lib/commonjs/reanimated2/layoutReanimation/LayoutAnimationRepository.js +23 -62
  38. package/lib/commonjs/reanimated2/layoutReanimation/LayoutAnimationRepository.js.map +1 -1
  39. package/lib/module/createAnimatedComponent.js +27 -26
  40. package/lib/module/createAnimatedComponent.js.map +1 -1
  41. package/lib/module/reanimated2/NativeReanimated/NativeReanimated.js +4 -0
  42. package/lib/module/reanimated2/NativeReanimated/NativeReanimated.js.map +1 -1
  43. package/lib/module/reanimated2/core.js +7 -9
  44. package/lib/module/reanimated2/core.js.map +1 -1
  45. package/lib/module/reanimated2/globals.d.js.map +1 -1
  46. package/lib/module/reanimated2/jestUtils.js +17 -9
  47. package/lib/module/reanimated2/jestUtils.js.map +1 -1
  48. package/lib/module/reanimated2/layoutReanimation/LayoutAnimationRepository.js +22 -61
  49. package/lib/module/reanimated2/layoutReanimation/LayoutAnimationRepository.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/createAnimatedComponent.tsx +48 -39
  52. package/src/reanimated2/NativeReanimated/NativeReanimated.ts +15 -0
  53. package/src/reanimated2/core.ts +19 -10
  54. package/src/reanimated2/globals.d.ts +10 -6
  55. package/src/reanimated2/jestUtils.ts +14 -6
  56. package/src/reanimated2/layoutReanimation/LayoutAnimationRepository.ts +24 -50
  57. package/lib/commonjs/reanimated2/layoutReanimation/defaultAnimations/Default.js +0 -55
  58. package/lib/commonjs/reanimated2/layoutReanimation/defaultAnimations/Default.js.map +0 -1
  59. package/lib/module/reanimated2/layoutReanimation/defaultAnimations/Default.js +0 -40
  60. package/lib/module/reanimated2/layoutReanimation/defaultAnimations/Default.js.map +0 -1
  61. package/src/reanimated2/layoutReanimation/defaultAnimations/Default.ts +0 -49
@@ -5,28 +5,38 @@
5
5
  #import <React/UIView+Private.h>
6
6
  #import <React/UIView+React.h>
7
7
 
8
- @interface REAAnimationsManager ()
9
-
10
8
  typedef NS_ENUM(NSInteger, FrameConfigType) { EnteringFrame, ExitingFrame };
11
9
 
12
- @property (atomic, nullable) void (^startAnimationForTag)(NSNumber *, NSString *, NSDictionary *, NSNumber *);
13
- @property (atomic, nullable) void (^removeConfigForTag)(NSNumber *);
10
+ static BOOL REANodeFind(id<RCTComponent> view, int (^block)(id<RCTComponent>))
11
+ {
12
+ if (!view.reactTag) {
13
+ return NO;
14
+ }
14
15
 
15
- - (void)removeLeftovers;
16
- - (void)scheduleCleaning;
17
- - (double)getDoubleOrZero:(NSNumber *)number;
16
+ if (block(view)) {
17
+ return YES;
18
+ }
18
19
 
19
- @end
20
+ for (id<RCTComponent> subview in view.reactSubviews) {
21
+ if (REANodeFind(subview, block)) {
22
+ return YES;
23
+ }
24
+ }
25
+
26
+ return NO;
27
+ }
20
28
 
21
29
  @implementation REAAnimationsManager {
22
30
  RCTUIManager *_uiManager;
23
31
  REAUIManager *_reaUiManager;
24
- NSMutableDictionary<NSNumber *, NSNumber *> *_states;
25
- NSMutableDictionary<NSNumber *, UIView *> *_viewForTag;
26
- NSMutableSet<NSNumber *> *_toRemove;
32
+ NSMutableDictionary<NSNumber *, UIView *> *_exitingViews;
33
+ NSMutableDictionary<NSNumber *, NSNumber *> *_exitingSubviewsCountMap;
34
+ NSMutableSet<NSNumber *> *_ancestorsToRemove;
27
35
  NSMutableArray<NSString *> *_targetKeys;
28
36
  NSMutableArray<NSString *> *_currentKeys;
29
- BOOL _cleaningScheduled;
37
+ REAAnimationStartingBlock _startAnimationForTag;
38
+ REAHasAnimationBlock _hasAnimationForTag;
39
+ REAAnimationRemovingBlock _clearAnimationConfigForTag;
30
40
  }
31
41
 
32
42
  + (NSArray *)layoutKeys
@@ -44,10 +54,9 @@ typedef NS_ENUM(NSInteger, FrameConfigType) { EnteringFrame, ExitingFrame };
44
54
  if (self = [super init]) {
45
55
  _uiManager = uiManager;
46
56
  _reaUiManager = (REAUIManager *)uiManager;
47
- _states = [NSMutableDictionary new];
48
- _viewForTag = [NSMutableDictionary new];
49
- _toRemove = [NSMutableSet new];
50
- _cleaningScheduled = false;
57
+ _exitingViews = [NSMutableDictionary new];
58
+ _exitingSubviewsCountMap = [NSMutableDictionary new];
59
+ _ancestorsToRemove = [NSMutableSet new];
51
60
 
52
61
  _targetKeys = [NSMutableArray new];
53
62
  _currentKeys = [NSMutableArray new];
@@ -61,138 +70,77 @@ typedef NS_ENUM(NSInteger, FrameConfigType) { EnteringFrame, ExitingFrame };
61
70
 
62
71
  - (void)invalidate
63
72
  {
73
+ for (NSNumber *tag in [[_exitingViews allKeys] copy]) {
74
+ [self endLayoutAnimationForTag:tag cancelled:true removeView:true];
75
+ }
64
76
  _startAnimationForTag = nil;
65
- _removeConfigForTag = nil;
77
+ _hasAnimationForTag = nil;
66
78
  _uiManager = nil;
67
- _states = nil;
68
- _viewForTag = nil;
69
- _toRemove = nil;
70
- _cleaningScheduled = false;
79
+ _exitingViews = nil;
71
80
  _targetKeys = nil;
72
81
  _currentKeys = nil;
73
82
  }
74
83
 
75
- - (void)setAnimationStartingBlock:
76
- (void (^)(NSNumber *tag, NSString *type, NSDictionary *yogaValues, NSNumber *depth))startAnimation
84
+ - (void)setAnimationStartingBlock:(REAAnimationStartingBlock)startAnimation
77
85
  {
78
86
  _startAnimationForTag = startAnimation;
79
87
  }
80
88
 
81
- - (void)setRemovingConfigBlock:(void (^)(NSNumber *tag))block
89
+ - (void)setHasAnimationBlock:(REAHasAnimationBlock)hasAnimation
82
90
  {
83
- _removeConfigForTag = block;
91
+ _hasAnimationForTag = hasAnimation;
84
92
  }
85
93
 
86
- - (void)scheduleCleaning
94
+ - (void)setAnimationRemovingBlock:(REAAnimationRemovingBlock)clearAnimation
87
95
  {
88
- if (_cleaningScheduled) {
89
- return;
90
- }
91
- _cleaningScheduled = true;
92
-
93
- __weak REAAnimationsManager *weakSelf = self;
94
- dispatch_async(dispatch_get_main_queue(), ^{
95
- self->_cleaningScheduled = false;
96
- if (weakSelf == nil) {
97
- return;
98
- }
99
- [self removeLeftovers];
100
- });
96
+ _clearAnimationConfigForTag = clearAnimation;
101
97
  }
102
98
 
103
- - (void)findRoot:(UIView *)view roots:(NSMutableSet<NSNumber *> *)roots
99
+ - (UIView *)viewForTag:(NSNumber *)tag
104
100
  {
105
- UIView *currentView = view;
106
- NSNumber *lastToRemoveTag = nil;
107
- while (currentView != nil) {
108
- ViewState state = [_states[currentView.reactTag] intValue];
109
- if (state == Disappearing) {
110
- return;
111
- }
112
- if (state == ToRemove) {
113
- lastToRemoveTag = currentView.reactTag;
114
- }
115
- currentView = currentView.superview;
116
- }
117
- if (lastToRemoveTag != nil) {
118
- [roots addObject:lastToRemoveTag];
101
+ UIView *view = [_reaUiManager viewForReactTag:tag];
102
+ if (view == nil) {
103
+ return [_exitingViews objectForKey:tag];
119
104
  }
105
+ return view;
120
106
  }
121
107
 
122
- - (BOOL)dfs:(UIView *)root view:(UIView *)view cands:(NSMutableSet<NSNumber *> *)cands
108
+ - (void)endLayoutAnimationForTag:(NSNumber *)tag cancelled:(BOOL)cancelled removeView:(BOOL)removeView
123
109
  {
124
- NSNumber *tag = view.reactTag;
125
- if (tag == nil) {
126
- return true;
110
+ UIView *view = [_exitingViews objectForKey:tag];
111
+ if (removeView && view != nil) {
112
+ [self endAnimationsRecursive:view];
113
+ [view removeFromSuperview];
127
114
  }
128
- if (![cands containsObject:tag] && _states[tag] != nil) {
129
- return true;
130
- }
131
- BOOL cannotStripe = false;
132
- NSArray<UIView *> *toRemoveCopy = [view.reactSubviews copy];
133
- for (UIView *child in toRemoveCopy) {
134
- if (![view isKindOfClass:[RCTTextView class]]) {
135
- cannotStripe |= [self dfs:root view:child cands:cands];
136
- }
137
- }
138
- if (!cannotStripe) {
139
- if (view.reactSuperview != nil) {
140
- [_reaUiManager unregisterView:view];
141
- }
142
- [_states removeObjectForKey:tag];
143
- [_viewForTag removeObjectForKey:tag];
144
- [_toRemove removeObject:tag];
145
- }
146
- return cannotStripe;
147
115
  }
148
116
 
149
- - (void)removeLeftovers
117
+ - (void)endAnimationsRecursive:(UIView *)view
150
118
  {
151
- NSMutableSet<NSNumber *> *roots = [NSMutableSet new];
152
- for (NSNumber *viewTag in _toRemove) {
153
- UIView *view = _viewForTag[viewTag];
154
- if (view == nil) {
155
- view = [_reaUiManager viewForReactTag:viewTag];
156
- _viewForTag[viewTag] = view;
157
- }
158
- [self findRoot:view roots:roots];
119
+ NSNumber *tag = [view reactTag];
120
+
121
+ if (tag == nil) {
122
+ return;
159
123
  }
160
- for (NSNumber *viewTag in roots) {
161
- UIView *view = _viewForTag[viewTag];
162
- [self dfs:view view:view cands:_toRemove];
124
+
125
+ // we'll remove this view anyway when exiting from recursion,
126
+ // no need to remove it in `maybeDropAncestors`
127
+ [_ancestorsToRemove removeObject:tag];
128
+
129
+ for (UIView *child in [[view subviews] copy]) {
130
+ [self endAnimationsRecursive:child];
163
131
  }
164
- }
165
132
 
166
- - (void)notifyAboutEnd:(NSNumber *)tag cancelled:(BOOL)cancelled
167
- {
168
- if (!cancelled) {
169
- ViewState state = [_states[tag] intValue];
170
- if (state == Appearing) {
171
- _states[tag] = [NSNumber numberWithInt:Layout];
172
- }
173
- if (state == Disappearing) {
174
- _states[tag] = [NSNumber numberWithInt:ToRemove];
175
- if (tag != nil) {
176
- [_toRemove addObject:tag];
177
- }
178
- [self scheduleCleaning];
179
- }
133
+ if ([_exitingViews objectForKey:tag]) {
134
+ [_exitingViews removeObjectForKey:tag];
135
+ [self maybeDropAncestors:view];
180
136
  }
181
137
  }
182
138
 
183
- - (void)notifyAboutProgress:(NSDictionary *)newStyle tag:(NSNumber *)tag
139
+ - (void)progressLayoutAnimationWithStyle:(NSDictionary *)newStyle forTag:(NSNumber *)tag
184
140
  {
185
- if (_states[tag] == nil) {
186
- return;
187
- }
188
- ViewState state = [_states[tag] intValue];
189
- if (state == Inactive) {
190
- _states[tag] = [NSNumber numberWithInt:Appearing];
191
- }
192
-
193
141
  NSMutableDictionary *dataComponenetsByName = [_uiManager valueForKey:@"_componentDataByName"];
194
142
  RCTComponentData *componentData = dataComponenetsByName[@"RCTView"];
195
- [self setNewProps:[newStyle mutableCopy] forView:_viewForTag[tag] withComponentData:componentData];
143
+ [self setNewProps:[newStyle mutableCopy] forView:[self viewForTag:tag] withComponentData:componentData];
196
144
  }
197
145
 
198
146
  - (double)getDoubleOrZero:(NSNumber *)number
@@ -288,73 +236,160 @@ typedef NS_ENUM(NSInteger, FrameConfigType) { EnteringFrame, ExitingFrame };
288
236
  return preparedData;
289
237
  }
290
238
 
291
- - (void)onViewRemoval:(UIView *)view before:(REASnapshot *)before
239
+ - (BOOL)wantsHandleRemovalOfView:(UIView *)view
292
240
  {
293
- NSNumber *tag = view.reactTag;
294
- ViewState state = [_states[tag] intValue];
295
- if (state == Disappearing || state == ToRemove || tag == nil) {
296
- return;
241
+ return REANodeFind(view, ^(id<RCTComponent> view) {
242
+ return [self->_exitingSubviewsCountMap objectForKey:view.reactTag] != nil ||
243
+ self->_hasAnimationForTag(view.reactTag, @"exiting");
244
+ });
245
+ }
246
+
247
+ - (void)registerExitingAncestors:(UIView *)child
248
+ {
249
+ UIView *parent = child.superview;
250
+ while (parent != nil && ![parent isKindOfClass:[RCTRootView class]]) {
251
+ if (parent.reactTag != nil) {
252
+ _exitingSubviewsCountMap[parent.reactTag] = @([_exitingSubviewsCountMap[parent.reactTag] intValue] + 1);
253
+ }
254
+ parent = parent.superview;
297
255
  }
298
- NSMutableDictionary<NSString *, NSObject *> *startValues = before.values;
299
- if (state == Inactive) {
300
- if (startValues != nil) {
301
- _states[tag] = [NSNumber numberWithInt:ToRemove];
302
- [_toRemove addObject:tag];
303
- [self scheduleCleaning];
256
+ }
257
+
258
+ - (void)maybeDropAncestors:(UIView *)child
259
+ {
260
+ UIView *parent = child.superview;
261
+ while (parent != nil && ![parent isKindOfClass:[RCTRootView class]]) {
262
+ UIView *view = parent;
263
+ parent = view.superview;
264
+ if (view.reactTag == nil) {
265
+ continue;
266
+ }
267
+ int trackingCount = [_exitingSubviewsCountMap[view.reactTag] intValue] - 1;
268
+ if (trackingCount <= 0) {
269
+ if ([_ancestorsToRemove containsObject:view.reactTag]) {
270
+ [_ancestorsToRemove removeObject:view.reactTag];
271
+ if (![_exitingViews objectForKey:view.reactTag]) {
272
+ [view removeFromSuperview];
273
+ }
274
+ }
275
+ [_exitingSubviewsCountMap removeObjectForKey:view.reactTag];
276
+ } else {
277
+ _exitingSubviewsCountMap[view.reactTag] = @(trackingCount);
304
278
  }
305
- return;
306
279
  }
307
- _states[tag] = [NSNumber numberWithInt:Disappearing];
308
- NSDictionary *preparedValues = [self prepareDataForAnimatingWorklet:startValues frameConfig:ExitingFrame];
309
- _startAnimationForTag(tag, @"exiting", preparedValues, @(0));
310
280
  }
311
281
 
312
- - (void)onViewCreate:(UIView *)view after:(REASnapshot *)after
282
+ - (BOOL)removeRecursive:(UIView *)view fromContainer:(UIView *)container withoutAnimation:(BOOL)removeImmediately;
313
283
  {
314
- _reaUiManager.flushUiOperations();
315
- NSNumber *tag = view.reactTag;
316
- if (_states[tag] == nil) {
317
- _states[tag] = [NSNumber numberWithInt:Inactive];
318
- _viewForTag[tag] = view;
284
+ if (!view.reactTag) {
285
+ return NO;
319
286
  }
320
- NSMutableDictionary *targetValues = after.values;
321
- ViewState state = [_states[tag] intValue];
322
- if (state == Inactive) {
323
- if (targetValues != nil) {
324
- NSDictionary *preparedValues = [self prepareDataForAnimatingWorklet:targetValues frameConfig:EnteringFrame];
325
- _startAnimationForTag(tag, @"entering", preparedValues, @(0));
287
+ BOOL hasExitAnimation = _hasAnimationForTag(view.reactTag, @"exiting") || [_exitingViews objectForKey:view.reactTag];
288
+ BOOL hasAnimatedChildren = NO;
289
+ removeImmediately = removeImmediately && !hasExitAnimation;
290
+ NSMutableArray *toBeRemoved = [[NSMutableArray alloc] init];
291
+
292
+ for (UIView *subview in [view.reactSubviews copy]) {
293
+ if ([self removeRecursive:subview fromContainer:view withoutAnimation:removeImmediately]) {
294
+ hasAnimatedChildren = YES;
295
+ } else if (removeImmediately) {
296
+ [toBeRemoved addObject:subview];
297
+ }
298
+ }
299
+
300
+ BOOL wantAnimateExit = hasExitAnimation || hasAnimatedChildren;
301
+
302
+ if (!wantAnimateExit) {
303
+ return NO;
304
+ }
305
+
306
+ REASnapshot *before;
307
+ if (hasExitAnimation) {
308
+ before = [[REASnapshot alloc] init:view];
309
+ }
310
+ // start exit animation
311
+ UIView *originalSuperview = view.superview;
312
+ NSUInteger originalIndex = [originalSuperview.subviews indexOfObjectIdenticalTo:view];
313
+ [container removeReactSubview:view];
314
+ // we don't want user interaction on exiting views
315
+ view.userInteractionEnabled = NO;
316
+ [originalSuperview insertSubview:view atIndex:originalIndex];
317
+
318
+ if (hasExitAnimation && ![_exitingViews objectForKey:view.reactTag]) {
319
+ NSDictionary *preparedValues = [self prepareDataForAnimatingWorklet:before.values frameConfig:ExitingFrame];
320
+ [_exitingViews setObject:view forKey:view.reactTag];
321
+ [self registerExitingAncestors:view];
322
+ _startAnimationForTag(view.reactTag, @"exiting", preparedValues, @(0));
323
+ }
324
+
325
+ if (hasAnimatedChildren) {
326
+ [_ancestorsToRemove addObject:view.reactTag];
327
+ }
328
+
329
+ for (UIView *child in toBeRemoved) {
330
+ [view removeReactSubview:child];
331
+ }
332
+
333
+ // NOTE: even though this view is still visible,
334
+ // since it's removed from the React tree, we won't
335
+ // start new animations for it, and might as well remove
336
+ // the layout animation config now
337
+ _clearAnimationConfigForTag(view.reactTag);
338
+ return YES;
339
+ }
340
+
341
+ - (void)removeChildren:(NSArray<UIView *> *)children fromContainer:(UIView *)container
342
+ {
343
+ for (UIView *removedChild in children) {
344
+ if (![self removeRecursive:removedChild fromContainer:container withoutAnimation:true]) {
345
+ [removedChild removeFromSuperview];
326
346
  }
327
- return;
328
347
  }
329
348
  }
330
349
 
350
+ - (void)onViewCreate:(UIView *)view after:(REASnapshot *)after
351
+ {
352
+ NSMutableDictionary *targetValues = after.values;
353
+ NSDictionary *preparedValues = [self prepareDataForAnimatingWorklet:targetValues frameConfig:EnteringFrame];
354
+ _startAnimationForTag(view.reactTag, @"entering", preparedValues, @(0));
355
+ }
356
+
331
357
  - (void)onViewUpdate:(UIView *)view before:(REASnapshot *)before after:(REASnapshot *)after
332
358
  {
333
- NSNumber *tag = view.reactTag;
334
359
  NSMutableDictionary *targetValues = after.values;
335
360
  NSMutableDictionary *currentValues = before.values;
336
- if (_states[tag] == nil) {
337
- return;
338
- }
339
- ViewState state = [_states[tag] intValue];
340
- if (state == Disappearing || state == ToRemove || state == Inactive) {
341
- return;
361
+
362
+ NSDictionary *preparedValues = [self prepareDataForLayoutAnimatingWorklet:currentValues targetValues:targetValues];
363
+ _startAnimationForTag(view.reactTag, @"layout", preparedValues, @(0));
364
+ }
365
+
366
+ - (REASnapshot *)prepareSnapshotBeforeMountForView:(UIView *)view
367
+ {
368
+ if (_hasAnimationForTag(view.reactTag, @"layout")) {
369
+ return [[REASnapshot alloc] init:view];
342
370
  }
343
- if (state == Appearing) {
344
- BOOL doNotStartLayout = true;
345
- for (int i = 0; i < [[self class] layoutKeys].count; ++i) {
346
- if ([((NSNumber *)currentValues[_currentKeys[i]]) doubleValue] !=
347
- [((NSNumber *)targetValues[_targetKeys[i]]) doubleValue]) {
348
- doNotStartLayout = false;
349
- }
350
- }
351
- if (doNotStartLayout) {
352
- return;
371
+ return nil;
372
+ }
373
+
374
+ - (void)viewDidMount:(UIView *)view withBeforeSnapshot:(nonnull REASnapshot *)before
375
+ {
376
+ NSString *type = before == nil ? @"entering" : @"layout";
377
+ if (_hasAnimationForTag(view.reactTag, type)) {
378
+ REASnapshot *after = [[REASnapshot alloc] init:view];
379
+ if (before == nil) {
380
+ [self onViewCreate:view after:after];
381
+ } else {
382
+ [self onViewUpdate:view before:before after:after];
353
383
  }
354
384
  }
355
- _states[view.reactTag] = [NSNumber numberWithInt:Layout];
356
- NSDictionary *preparedValues = [self prepareDataForLayoutAnimatingWorklet:currentValues targetValues:targetValues];
357
- _startAnimationForTag(view.reactTag, @"layout", preparedValues, @(0));
385
+ }
386
+
387
+ - (void)removeAnimationsFromSubtree:(UIView *)view
388
+ {
389
+ REANodeFind(view, ^int(id<RCTComponent> view) {
390
+ self->_clearAnimationConfigForTag(view.reactTag);
391
+ return false;
392
+ });
358
393
  }
359
394
 
360
395
  @end
@@ -24,17 +24,25 @@
24
24
  removeAtIndices:(NSArray<NSNumber *> *)removeAtIndices
25
25
  registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry;
26
26
 
27
+ - (void)_removeChildren:(NSArray<UIView *> *)children fromContainer:(UIView *)container;
28
+
29
+ - (void)_removeChildren:(NSArray<UIView *> *)children
30
+ fromContainer:(UIView *)container
31
+ withAnimation:(RCTLayoutAnimationGroup *)animation;
32
+
33
+ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView;
34
+
27
35
  - (NSArray<id<RCTComponent>> *)_childrenToRemoveFromContainer:(id<RCTComponent>)container
28
36
  atIndices:(NSArray<NSNumber *> *)atIndices;
29
37
  @end
30
38
 
31
- @implementation REAUIManager
32
-
33
- BOOL blockSetter = false;
34
- NSMutableDictionary<NSNumber *, NSMutableSet<id<RCTComponent>> *> *_toBeRemovedRegister;
35
- NSMutableDictionary<NSNumber *, NSNumber *> *_parentMapper;
36
- REAAnimationsManager *_animationsManager;
37
- std::weak_ptr<reanimated::Scheduler> _scheduler;
39
+ @implementation REAUIManager {
40
+ RCTLayoutAnimationGroup *_reactLayoutAnimationGroup;
41
+ NSMutableDictionary<NSNumber *, NSMutableSet<id<RCTComponent>> *> *_toBeRemovedRegister;
42
+ NSMutableDictionary<NSNumber *, NSNumber *> *_parentMapper;
43
+ REAAnimationsManager *_animationsManager;
44
+ std::weak_ptr<reanimated::Scheduler> _scheduler;
45
+ }
38
46
 
39
47
  + (NSString *)moduleName
40
48
  {
@@ -43,6 +51,13 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
43
51
 
44
52
  - (void)setBridge:(RCTBridge *)bridge
45
53
  {
54
+ // setting a layout animation group with a deleting animation in order to
55
+ // allows us to call a different method in RCTUIManager for cleaning up exiting views
56
+ RCTLayoutAnimation *deletingAnimation = [[RCTLayoutAnimation alloc] initWithDuration:0 config:@{}];
57
+ _reactLayoutAnimationGroup = [[RCTLayoutAnimationGroup alloc] initWithCreatingLayoutAnimation:nil
58
+ updatingLayoutAnimation:nil
59
+ deletingLayoutAnimation:deletingAnimation
60
+ callback:nil];
46
61
  if (!_blockSetter) {
47
62
  _blockSetter = true;
48
63
 
@@ -64,6 +79,21 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
64
79
  }
65
80
  }
66
81
 
82
+ - (void)_removeChildren:(NSArray<UIView *> *)children
83
+ fromContainer:(UIView *)container
84
+ withAnimation:(RCTLayoutAnimationGroup *)animation
85
+ {
86
+ if (animation == _reactLayoutAnimationGroup) {
87
+ // if a removed view in this batch has an `exiting` animation,
88
+ // let REAAnimationsManager handle the removal
89
+ [_animationsManager removeChildren:children fromContainer:container];
90
+ } else {
91
+ // otherwise, if there's a layout animation group set,
92
+ // delegate to the React Native implementation for layout animations
93
+ [super _removeChildren:children fromContainer:container withAnimation:animation];
94
+ }
95
+ }
96
+
67
97
  - (void)_manageChildren:(NSNumber *)containerTag
68
98
  moveFromIndices:(NSArray<NSNumber *> *)moveFromIndices
69
99
  moveToIndices:(NSArray<NSNumber *> *)moveToIndices
@@ -85,22 +115,23 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
85
115
 
86
116
  // Reanimated changes /start
87
117
  BOOL isUIViewRegistry = ((id)registry == (id)[self valueForKey:@"_viewRegistry"]);
88
- id<RCTComponent> container;
89
- NSMutableArray<id<RCTComponent>> *permanentlyRemovedChildren;
90
118
  if (isUIViewRegistry) {
91
- container = registry[containerTag];
92
- for (id<RCTComponent> toRemoveChild in _toBeRemovedRegister[containerTag]) {
93
- [container removeReactSubview:toRemoveChild];
94
- }
95
-
96
- permanentlyRemovedChildren = (NSMutableArray *)[super _childrenToRemoveFromContainer:container
97
- atIndices:removeAtIndices];
98
- if (permanentlyRemovedChildren != nil) {
99
- for (id<RCTComponent> permanentlyRemovedChild in permanentlyRemovedChildren) {
100
- if (_toBeRemovedRegister[containerTag] == nil) {
101
- _toBeRemovedRegister[containerTag] = [[NSMutableSet<id<RCTComponent>> alloc] init];
119
+ BOOL wasProxyRemovalSet = [self valueForKey:@"_layoutAnimationGroup"] == _reactLayoutAnimationGroup;
120
+ BOOL wantProxyRemoval = NO;
121
+ if (!wasProxyRemovalSet) {
122
+ id<RCTComponent> container = registry[containerTag];
123
+ NSArray<id<RCTComponent>> *permanentlyRemovedChildren = [self _childrenToRemoveFromContainer:container
124
+ atIndices:removeAtIndices];
125
+ for (UIView *removedChild in permanentlyRemovedChildren) {
126
+ if ([_animationsManager wantsHandleRemovalOfView:removedChild]) {
127
+ wantProxyRemoval = YES;
128
+ break;
102
129
  }
103
- [_toBeRemovedRegister[containerTag] addObject:permanentlyRemovedChild];
130
+ [_animationsManager removeAnimationsFromSubtree:removedChild];
131
+ }
132
+ if (wantProxyRemoval) {
133
+ // set layout animation group
134
+ [super setNextLayoutAnimationGroup:_reactLayoutAnimationGroup];
104
135
  }
105
136
  }
106
137
  }
@@ -113,43 +144,11 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
113
144
  addAtIndices:addAtIndices
114
145
  removeAtIndices:removeAtIndices
115
146
  registry:registry];
116
-
117
- // Reanimated changes /start
118
- if (isUIViewRegistry) {
119
- NSMutableDictionary<NSNumber *, id<RCTComponent>> *viewRegistry = [self valueForKey:@"_viewRegistry"];
120
- for (id<RCTComponent> toRemoveChild in _toBeRemovedRegister[containerTag]) {
121
- NSInteger lastIndex = [container reactSubviews].count - 1;
122
- if (lastIndex < 0) {
123
- lastIndex = 0;
124
- }
125
- if ([toRemoveChild isKindOfClass:[RCTModalHostView class]]
126
- #if __has_include(<RNScreens/RNSScreen.h>)
127
- || ([toRemoveChild isKindOfClass:[RNSScreenView class]])
128
- #endif
129
- ) {
130
- // we don't want layout animations when removing modals or Screens of native-stack since it brings buggy
131
- // behavior
132
- [_toBeRemovedRegister[container.reactTag] removeObject:toRemoveChild];
133
- [permanentlyRemovedChildren removeObject:toRemoveChild];
134
-
135
- } else {
136
- [container insertReactSubview:toRemoveChild atIndex:lastIndex];
137
- viewRegistry[toRemoveChild.reactTag] = toRemoveChild;
138
- }
139
- }
140
-
141
- for (UIView *removedChild in permanentlyRemovedChildren) {
142
- [self callAnimationForTree:removedChild parentTag:containerTag];
143
- }
144
- }
145
- // Reanimated changes /end
146
147
  }
147
148
 
148
149
  - (void)callAnimationForTree:(UIView *)view parentTag:(NSNumber *)parentTag
149
150
  {
150
- REASnapshot *snapshot = [[REASnapshot alloc] init:view];
151
151
  _parentMapper[view.reactTag] = parentTag;
152
- [_animationsManager onViewRemoval:view before:snapshot];
153
152
 
154
153
  for (UIView *subView in view.reactSubviews) {
155
154
  [self callAnimationForTree:subView parentTag:view.reactTag];
@@ -159,6 +158,10 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
159
158
  // Overrided https://github.com/facebook/react-native/blob/v0.65.0/React/Modules/RCTUIManager.m#L530
160
159
  - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
161
160
  {
161
+ if (!reanimated::FeaturesConfig::isLayoutAnimationEnabled()) {
162
+ return [super uiBlockWithLayoutUpdateForRootView:rootShadowView];
163
+ }
164
+
162
165
  NSHashTable<RCTShadowView *> *affectedShadowViews = [NSHashTable weakObjectsHashTable];
163
166
  [rootShadowView layoutWithAffectedShadowViews:affectedShadowViews];
164
167
 
@@ -270,10 +273,7 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
270
273
  }
271
274
 
272
275
  // Reanimated changes /start
273
- REASnapshot *snapshotBefore;
274
- if (reanimated::FeaturesConfig::isLayoutAnimationEnabled()) {
275
- snapshotBefore = [[REASnapshot alloc] init:view];
276
- }
276
+ REASnapshot *snapshotBefore = isNew ? nil : [self->_animationsManager prepareSnapshotBeforeMountForView:view];
277
277
  // Reanimated changes /end
278
278
 
279
279
  if (creatingLayoutAnimation) {
@@ -322,22 +322,15 @@ std::weak_ptr<reanimated::Scheduler> _scheduler;
322
322
  }
323
323
 
324
324
  // Reanimated changes /start
325
- if (reanimated::FeaturesConfig::isLayoutAnimationEnabled()) {
326
- if (isNew) {
327
- REASnapshot *snapshot = [[REASnapshot alloc] init:view];
328
- [_animationsManager onViewCreate:view after:snapshot];
329
- } else {
330
- REASnapshot *snapshotAfter = [[REASnapshot alloc] init:view];
331
- [_animationsManager onViewUpdate:view before:snapshotBefore after:snapshotAfter];
332
- }
325
+ if (isNew || snapshotBefore != nil) {
326
+ [self->_animationsManager viewDidMount:view withBeforeSnapshot:snapshotBefore];
333
327
  }
328
+ // Reanimated changes /end
334
329
  }
335
-
336
- [_animationsManager removeLeftovers];
337
330
  // Clean up
338
- // uiManager->_layoutAnimationGroup = nil;
339
- [uiManager setValue:nil forKey:@"_layoutAnimationGroup"];
340
- // Reanimated changes /end
331
+ // below line serves as this one uiManager->_layoutAnimationGroup = nil;, because we don't have access to the
332
+ // private field
333
+ [uiManager setNextLayoutAnimationGroup:nil];
341
334
  };
342
335
  }
343
336