-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Swizzle RCTUIManager #4619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Swizzle RCTUIManager #4619
Changes from 8 commits
80d3956
a034515
52f28bc
7121862
bad1d4e
8e57036
f5a285c
3fdb104
b0aad16
eccd3db
ed61f72
82299af
e9a4ac4
af16d23
5e54936
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| #import <RNReanimated/REAAnimationsManager.h> | ||
|
|
||
| @interface REASwizzledUIManager : NSObject | ||
| - (instancetype)initWithUIManager:(RCTUIManager *)uiManager | ||
| withAnimatioinManager:(REAAnimationsManager *)animationsManager; | ||
| @end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| #import <Foundation/Foundation.h> | ||
| #import <RNReanimated/FeaturesConfig.h> | ||
| #import <RNReanimated/REAIOSScheduler.h> | ||
| #import <RNReanimated/REAUIManager.h> | ||
| #import <RNReanimated/REASwizzledUIManager.h> | ||
| #import <RNReanimated/REAUtils.h> | ||
| #import <RNReanimated/Scheduler.h> | ||
| #import <React/RCTComponentData.h> | ||
| #import <React/RCTLayoutAnimation.h> | ||
|
|
@@ -16,76 +17,65 @@ | |
| #endif | ||
|
|
||
| @interface RCTUIManager (REA) | ||
| - (void)_manageChildren:(NSNumber *)containerTag | ||
| moveFromIndices:(NSArray<NSNumber *> *)moveFromIndices | ||
| moveToIndices:(NSArray<NSNumber *> *)moveToIndices | ||
| addChildReactTags:(NSArray<NSNumber *> *)addChildReactTags | ||
| addAtIndices:(NSArray<NSNumber *> *)addAtIndices | ||
| removeAtIndices:(NSArray<NSNumber *> *)removeAtIndices | ||
| registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry; | ||
|
|
||
| - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView; | ||
|
|
||
| - (NSArray<id<RCTComponent>> *)_childrenToRemoveFromContainer:(id<RCTComponent>)container | ||
| atIndices:(NSArray<NSNumber *> *)atIndices; | ||
| @end | ||
|
|
||
| @implementation REAUIManager { | ||
| NSMutableDictionary<NSNumber *, NSMutableSet<id<RCTComponent>> *> *_toBeRemovedRegister; | ||
| NSMutableDictionary<NSNumber *, NSNumber *> *_parentMapper; | ||
| @implementation REASwizzledUIManager { | ||
| REAAnimationsManager *_animationsManager; | ||
| std::weak_ptr<reanimated::Scheduler> _scheduler; | ||
| RCTUIManager *_uiManager; | ||
| } | ||
|
|
||
| + (NSString *)moduleName | ||
| { | ||
| return NSStringFromClass([RCTUIManager class]); | ||
| } | ||
| static REASwizzledUIManager *_reaUIManager; | ||
|
|
||
| - (void)invalidate | ||
| - (instancetype)initWithUIManager:(RCTUIManager *)uiManager | ||
| withAnimatioinManager:(REAAnimationsManager *)animationsManager | ||
| { | ||
| [_animationsManager invalidate]; | ||
| [super invalidate]; | ||
| if (self = [super init]) { | ||
| _animationsManager = animationsManager; | ||
| _uiManager = uiManager; | ||
| _reaUIManager = self; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite understand the need for this whole REASwizzledUIManager class to exists. Also, assigning singletons like that seem a bit risky. It seem to me that the reason it's been done this way was for the swizzled methods to be able to access _uiManager and _animationsManager fields. I think the former can just be replaced by As for the latter field, the best way IMO would be to use category on UIManager class that adds animationsManager property to it using associated object technique (https://nshipster.com/associated-objects/)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| [self swizzleMethods]; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (void)setBridge:(RCTBridge *)bridge | ||
| - (void)swizzleMethods | ||
| { | ||
| if (!_blockSetter) { | ||
| _blockSetter = true; | ||
|
|
||
| self.bridge = bridge; | ||
| [super setValue:bridge forKey:@"_bridge"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_shadowViewRegistry"] forKey:@"_shadowViewRegistry"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_viewRegistry"] forKey:@"_viewRegistry"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_nativeIDRegistry"] forKey:@"_nativeIDRegistry"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_shadowViewsWithUpdatedProps"] | ||
| forKey:@"_shadowViewsWithUpdatedProps"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_shadowViewsWithUpdatedChildren"] | ||
| forKey:@"_shadowViewsWithUpdatedChildren"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_pendingUIBlocks"] forKey:@"_pendingUIBlocks"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_rootViewTags"] forKey:@"_rootViewTags"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_observerCoordinator"] forKey:@"_observerCoordinator"]; | ||
| [self setValue:[bridge.uiManager valueForKey:@"_componentDataByName"] forKey:@"_componentDataByName"]; | ||
|
|
||
| _blockSetter = false; | ||
| } | ||
| static dispatch_once_t onceToken; | ||
| dispatch_once(&onceToken, ^{ | ||
| [REAUtils swizzleMethod:@selector(uiBlockWithLayoutUpdateForRootView:) | ||
| forClass:[RCTUIManager class] | ||
| with:@selector(reanimated_uiBlockWithLayoutUpdateForRootView:) | ||
| fromClass:[self class]]; | ||
| SEL manageChildrenOriginal = @selector | ||
| (_manageChildren:moveFromIndices:moveToIndices:addChildReactTags:addAtIndices:removeAtIndices:registry:); | ||
| SEL manageChildrenReaniamted = | ||
| @selector(reanimated_manageChildren: | ||
| moveFromIndices:moveToIndices:addChildReactTags:addAtIndices:removeAtIndices:registry:); | ||
| [REAUtils swizzleMethod:manageChildrenOriginal | ||
| forClass:[RCTUIManager class] | ||
| with:manageChildrenReaniamted | ||
|
piaskowyk marked this conversation as resolved.
Outdated
|
||
| fromClass:[self class]]; | ||
| }); | ||
| } | ||
|
|
||
| - (void)_manageChildren:(NSNumber *)containerTag | ||
| moveFromIndices:(NSArray<NSNumber *> *)moveFromIndices | ||
| moveToIndices:(NSArray<NSNumber *> *)moveToIndices | ||
| addChildReactTags:(NSArray<NSNumber *> *)addChildReactTags | ||
| addAtIndices:(NSArray<NSNumber *> *)addAtIndices | ||
| removeAtIndices:(NSArray<NSNumber *> *)removeAtIndices | ||
| registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry | ||
| - (void)reanimated_manageChildren:(NSNumber *)containerTag | ||
| moveFromIndices:(NSArray<NSNumber *> *)moveFromIndices | ||
| moveToIndices:(NSArray<NSNumber *> *)moveToIndices | ||
| addChildReactTags:(NSArray<NSNumber *> *)addChildReactTags | ||
| addAtIndices:(NSArray<NSNumber *> *)addAtIndices | ||
| removeAtIndices:(NSArray<NSNumber *> *)removeAtIndices | ||
| registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry | ||
| { | ||
| bool isLayoutAnimationEnabled = reanimated::FeaturesConfig::isLayoutAnimationEnabled(); | ||
| id<RCTComponent> container; | ||
| NSArray<id<RCTComponent>> *permanentlyRemovedChildren; | ||
| BOOL containerIsRootOfViewController = NO; | ||
| if (isLayoutAnimationEnabled) { | ||
| container = registry[containerTag]; | ||
| permanentlyRemovedChildren = [self _childrenToRemoveFromContainer:container atIndices:removeAtIndices]; | ||
| permanentlyRemovedChildren = [_reaUIManager->_uiManager _childrenToRemoveFromContainer:container | ||
| atIndices:removeAtIndices]; | ||
|
|
||
| if ([container isKindOfClass:[UIView class]]) { | ||
| UIViewController *controller = ((UIView *)container).reactViewController; | ||
|
|
@@ -97,22 +87,23 @@ - (void)_manageChildren:(NSNumber *)containerTag | |
| // of some view controller. In that case, we skip running exiting animations | ||
| // in its children, to prevent issues with RN Screens. | ||
| if (containerIsRootOfViewController) { | ||
| NSArray<id<RCTComponent>> *permanentlyRemovedChildren = [self _childrenToRemoveFromContainer:container | ||
| atIndices:removeAtIndices]; | ||
| NSArray<id<RCTComponent>> *permanentlyRemovedChildren = | ||
| [_reaUIManager->_uiManager _childrenToRemoveFromContainer:container atIndices:removeAtIndices]; | ||
| for (UIView *view in permanentlyRemovedChildren) { | ||
| [_animationsManager endAnimationsRecursive:view]; | ||
| [_reaUIManager->_animationsManager endAnimationsRecursive:view]; | ||
| } | ||
| [_animationsManager removeAnimationsFromSubtree:(UIView *)container]; | ||
| [_reaUIManager->_animationsManager removeAnimationsFromSubtree:(UIView *)container]; | ||
| } | ||
| } | ||
|
|
||
| [super _manageChildren:containerTag | ||
| moveFromIndices:moveFromIndices | ||
| moveToIndices:moveToIndices | ||
| addChildReactTags:addChildReactTags | ||
| addAtIndices:addAtIndices | ||
| removeAtIndices:removeAtIndices | ||
| registry:registry]; | ||
| // call original method | ||
| [self reanimated_manageChildren:containerTag | ||
| moveFromIndices:moveFromIndices | ||
| moveToIndices:moveToIndices | ||
| addChildReactTags:addChildReactTags | ||
| addAtIndices:addAtIndices | ||
| removeAtIndices:removeAtIndices | ||
| registry:registry]; | ||
|
|
||
| if (!isLayoutAnimationEnabled) { | ||
| return; | ||
|
|
@@ -132,25 +123,15 @@ - (void)_manageChildren:(NSNumber *)containerTag | |
| return [(NSNumber *)obj1[0] compare:(NSNumber *)obj2[0]]; | ||
| }]; | ||
|
|
||
| [_animationsManager reattachAnimatedChildren:permanentlyRemovedChildren | ||
| toContainer:container | ||
| atIndices:removeAtIndices]; | ||
| } | ||
|
|
||
| - (void)callAnimationForTree:(UIView *)view parentTag:(NSNumber *)parentTag | ||
| { | ||
| _parentMapper[view.reactTag] = parentTag; | ||
|
|
||
| for (UIView *subView in view.reactSubviews) { | ||
| [self callAnimationForTree:subView parentTag:view.reactTag]; | ||
| } | ||
| [_reaUIManager->_animationsManager reattachAnimatedChildren:permanentlyRemovedChildren | ||
| toContainer:container | ||
| atIndices:removeAtIndices]; | ||
| } | ||
|
|
||
| // Overrided https://github.com/facebook/react-native/blob/v0.65.0/React/Modules/RCTUIManager.m#L530 | ||
| - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView | ||
| - (RCTViewManagerUIBlock)reanimated_uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView | ||
| { | ||
| if (!reanimated::FeaturesConfig::isLayoutAnimationEnabled()) { | ||
| return [super uiBlockWithLayoutUpdateForRootView:rootShadowView]; | ||
| return [self reanimated_uiBlockWithLayoutUpdateForRootView:rootShadowView]; | ||
| } | ||
|
|
||
| NSHashTable<RCTShadowView *> *affectedShadowViews = [NSHashTable weakObjectsHashTable]; | ||
|
|
@@ -266,7 +247,8 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView * | |
| } | ||
|
|
||
| // Reanimated changes /start | ||
| REASnapshot *snapshotBefore = isNew ? nil : [self->_animationsManager prepareSnapshotBeforeMountForView:view]; | ||
| REASnapshot *snapshotBefore = | ||
| isNew ? nil : [_reaUIManager->_animationsManager prepareSnapshotBeforeMountForView:view]; | ||
| snapshotsBefore[reactTag] = snapshotBefore; | ||
| // Reanimated changes /end | ||
|
|
||
|
|
@@ -327,7 +309,7 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView * | |
| REASnapshot *snapshotBefore = snapshotsBefore[reactTag]; | ||
|
|
||
| if (isNew || snapshotBefore != nil) { | ||
| [self->_animationsManager viewDidMount:view withBeforeSnapshot:snapshotBefore withNewFrame:frame]; | ||
| [_reaUIManager->_animationsManager viewDidMount:view withBeforeSnapshot:snapshotBefore withNewFrame:frame]; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -336,52 +318,9 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView * | |
| // private field | ||
| [uiManager setNextLayoutAnimationGroup:nil]; | ||
|
|
||
| [self->_animationsManager viewsDidLayout]; | ||
| [_reaUIManager->_animationsManager viewsDidLayout]; | ||
| // Reanimated changes /end | ||
| }; | ||
| } | ||
|
|
||
| - (Class)class | ||
| { | ||
| return [RCTUIManager class]; | ||
| } | ||
|
|
||
| + (Class)class | ||
| { | ||
| return [RCTUIManager class]; | ||
| } | ||
|
|
||
| - (void)setUp:(REAAnimationsManager *)animationsManager | ||
| { | ||
| _animationsManager = animationsManager; | ||
| _toBeRemovedRegister = [[NSMutableDictionary<NSNumber *, NSMutableSet<id<RCTComponent>> *> alloc] init]; | ||
| _parentMapper = [[NSMutableDictionary<NSNumber *, NSNumber *> alloc] init]; | ||
| } | ||
|
|
||
| - (void)unregisterView:(id<RCTComponent>)view | ||
| { | ||
| NSNumber *tag = _parentMapper[view.reactTag]; | ||
| if (tag == nil) { | ||
| return; | ||
| } | ||
|
|
||
| [_toBeRemovedRegister[tag] removeObject:view]; | ||
| if (_toBeRemovedRegister[tag].count == 0) { | ||
| [_toBeRemovedRegister removeObjectForKey:tag]; | ||
| } | ||
| NSMutableDictionary<NSNumber *, id<RCTComponent>> *viewRegistry = [self valueForKey:@"_viewRegistry"]; | ||
| [view.reactSuperview removeReactSubview:view]; | ||
| id<RCTComponent> parentView = viewRegistry[tag]; | ||
| @try { | ||
| [parentView removeReactSubview:view]; | ||
| } @catch (id anException) { | ||
| } | ||
| #if __has_include(<RNScreens/RNSScreen.h>) | ||
| if ([view isKindOfClass:[RNSScreenView class]]) { | ||
| [parentView didUpdateReactSubviews]; | ||
| } | ||
| #endif | ||
| [viewRegistry removeObjectForKey:view.reactTag]; | ||
| } | ||
|
|
||
| @end | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #import <REAUtils.h> | ||
| #import <objc/runtime.h> | ||
|
|
||
| @implementation REAUtils | ||
|
|
||
| + (void)swizzleMethod:(SEL)originalSelector | ||
| forClass:(Class)originalClass | ||
| with:(SEL)newSelector | ||
| fromClass:(Class)newClass | ||
| { | ||
| Method originalMethod = class_getInstanceMethod(originalClass, originalSelector); | ||
| Method newMethod = class_getInstanceMethod(newClass, newSelector); | ||
| IMP originalImplementation = method_getImplementation(originalMethod); | ||
| IMP newImplementation = method_getImplementation(newMethod); | ||
| class_replaceMethod(originalClass, newSelector, originalImplementation, method_getTypeEncoding(originalMethod)); | ||
| class_replaceMethod(originalClass, originalSelector, newImplementation, method_getTypeEncoding(newMethod)); | ||
| } | ||
|
|
||
| @end |
Uh oh!
There was an error while loading. Please reload this page.