mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-26 23:05:00 +08:00
Native Animated - Restore default values when removing props on iOS
Summary: This fixes a bug that causes properties to keep stale values because they were not restored to their default after being removed when their value was controlled by native animated. To fix this we restore default values in `disconnectFromView` by updating views with null values for all props that we modified previously. However this causes another issue where we lose any props that were set by the normal process because NativeAnimated operations are always executed after UIManager operatations. To fix this I added a way to hook into UIManager view updating process to be able to execute NativeAnimated operations either before or after updating native views. In the case of disconnecting we want to do it before updating views so that it does: Value changed by native animated -> value restored to default -> (optional) value updated by normal prop. This PR also depends on #10658. **Test plan** Tested that this fixed a particular bug in an app that uses ex-navigation + native animations where a navbar w Closes https://github.com/facebook/react-native/pull/11819 Differential Revision: D4752566 Pulled By: javache fbshipit-source-id: 68ee28200ffeba859ae1b98ac753bd7dcb8910f0
This commit is contained in:
committed by
Facebook Github Bot
parent
acc1edd188
commit
c9fae2fb93
@@ -48,6 +48,23 @@ RCT_EXTERN NSString *const RCTUIManagerDidRemoveRootViewNotification;
|
||||
*/
|
||||
RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
|
||||
|
||||
@class RCTUIManager;
|
||||
|
||||
/**
|
||||
* Allows to hook into UIManager internals. This can be used to execute code at
|
||||
* specific points during the view updating process.
|
||||
*/
|
||||
@protocol RCTUIManagerObserver <NSObject>
|
||||
|
||||
/**
|
||||
* Called before flushing UI blocks at the end of a batch. Note that this won't
|
||||
* get called for partial batches when using `unsafeFlushUIChangesBeforeBatchEnds`.
|
||||
* This is called from the UIManager queue. Can be used to add UI operations in that batch.
|
||||
*/
|
||||
- (void)uiManagerWillFlushUIBlocks:(RCTUIManager *)manager;
|
||||
|
||||
@end
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
|
||||
/**
|
||||
@@ -105,6 +122,23 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
|
||||
*/
|
||||
- (void)addUIBlock:(RCTViewManagerUIBlock)block;
|
||||
|
||||
/**
|
||||
* Schedule a block to be executed on the UI thread. Useful if you need to execute
|
||||
* view logic before all currently queued view updates have completed.
|
||||
*/
|
||||
- (void)prependUIBlock:(RCTViewManagerUIBlock)block;
|
||||
|
||||
/**
|
||||
* Add a UIManagerObserver. See the RCTUIManagerObserver protocol for more info. This
|
||||
* method can be called safely from any queue.
|
||||
*/
|
||||
- (void)addUIManagerObserver:(id<RCTUIManagerObserver>)observer;
|
||||
|
||||
/**
|
||||
* Remove a UIManagerObserver. This method can be called safely from any queue.
|
||||
*/
|
||||
- (void)removeUIManagerObserver:(id<RCTUIManagerObserver>)observer;
|
||||
|
||||
/**
|
||||
* Used by native animated module to bypass the process of updating the values through the shadow
|
||||
* view hierarchy. This method will directly update native views, which means that updates for
|
||||
|
||||
@@ -225,6 +225,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
|
||||
NSDictionary *_componentDataByName;
|
||||
|
||||
NSMutableSet<id<RCTComponent>> *_bridgeTransactionListeners;
|
||||
NSMutableSet<id<RCTUIManagerObserver>> *_uiManagerObservers;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@@ -305,6 +306,7 @@ RCT_EXPORT_MODULE()
|
||||
_rootViewTags = [NSMutableSet new];
|
||||
|
||||
_bridgeTransactionListeners = [NSMutableSet new];
|
||||
_uiManagerObservers = [NSMutableSet new];
|
||||
|
||||
_viewsToBeDeleted = [NSMutableSet new];
|
||||
|
||||
@@ -501,6 +503,19 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
|
||||
[_pendingUIBlocks addObject:block];
|
||||
}
|
||||
|
||||
- (void)prependUIBlock:(RCTViewManagerUIBlock)block
|
||||
{
|
||||
RCTAssertThread(RCTGetUIManagerQueue(),
|
||||
@"-[RCTUIManager prependUIBlock:] should only be called from the "
|
||||
"UIManager's queue (get this using `RCTGetUIManagerQueue()`)");
|
||||
|
||||
if (!block || !_viewRegistry) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_pendingUIBlocks insertObject:block atIndex:0];
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
|
||||
{
|
||||
RCTAssert(!RCTIsMainQueue(), @"Should be called on shadow queue");
|
||||
@@ -698,6 +713,20 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addUIManagerObserver:(id<RCTUIManagerObserver>)observer
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self->_uiManagerObservers addObject:observer];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeUIManagerObserver:(id<RCTUIManagerObserver>)observer
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self->_uiManagerObservers removeObject:observer];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to be called from JS, which takes a container ID and then releases
|
||||
* all subviews for that container upon receipt.
|
||||
@@ -1139,6 +1168,10 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
|
||||
}
|
||||
}];
|
||||
|
||||
for (id<RCTUIManagerObserver> observer in _uiManagerObservers) {
|
||||
[observer uiManagerWillFlushUIBlocks:self];
|
||||
}
|
||||
|
||||
[self flushUIBlocks];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user