Skip to content

Commit

Permalink
RCTUIManagerObserverCoordinator: new way to subscribe for granular no…
Browse files Browse the repository at this point in the history
…tifications from UI Manager

Reviewed By: majak

Differential Revision: D4868591

fbshipit-source-id: 24a09ffa3e69dec5ce1f0a8715c7e4701d781996
  • Loading branch information
shergin authored and facebook-github-bot committed May 8, 2017
1 parent 2a98432 commit 712b1dd
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 56 deletions.
1 change: 1 addition & 0 deletions Libraries/NativeAnimation/RCTNativeAnimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <React/RCTEventDispatcher.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTUIManager.h>
#import <React/RCTUIManagerObserverCoordinator.h>

#import "RCTValueAnimatedNode.h"

Expand Down
6 changes: 4 additions & 2 deletions Libraries/NativeAnimation/RCTNativeAnimatedModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ - (void)invalidate
{
[_nodesManager stopAnimationLoop];
[self.bridge.eventDispatcher removeDispatchObserver:self];
[self.bridge.uiManager removeUIManagerObserver:self];
[self.bridge.uiManager.observerCoordinator removeObserver:self];
}

- (dispatch_queue_t)methodQueue
Expand All @@ -48,7 +48,7 @@ - (void)setBridge:(RCTBridge *)bridge
_preOperations = [NSMutableArray new];

[bridge.eventDispatcher addDispatchObserver:self];
[bridge.uiManager addUIManagerObserver:self];
[bridge.uiManager.observerCoordinator addObserver:self];
}

#pragma mark -- API
Expand Down Expand Up @@ -196,6 +196,8 @@ - (void)addPreOperationBlock:(AnimatedOperation)operation
[_preOperations addObject:operation];
}

#pragma mark - RCTUIManagerObserver

- (void)uiManagerWillFlushUIBlocks:(RCTUIManager *)uiManager
{
if (_preOperations.count == 0 && _operations.count == 0) {
Expand Down
36 changes: 7 additions & 29 deletions React/Modules/RCTUIManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,7 @@ 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;
@class RCTUIManagerObserverCoordinator;

/**
* The RCTUIManager is the module responsible for updating the view hierarchy.
Expand Down Expand Up @@ -139,17 +122,6 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
*/
- (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
Expand Down Expand Up @@ -192,6 +164,12 @@ RCT_EXTERN NSString *const RCTUIManagerRootViewKey;
*/
- (void)setNeedsLayout;

/**
* Dedicated object for subscribing for UIManager events.
* See `RCTUIManagerObserver` protocol for more details.
*/
@property (atomic, retain, readonly) RCTUIManagerObserverCoordinator *observerCoordinator;

@end

@interface RCTUIManager (Deprecated)
Expand Down
31 changes: 12 additions & 19 deletions React/Modules/RCTUIManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#import "RCTScrollableProtocol.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"
#import "RCTUIManagerObserverCoordinator.h"
#import "RCTView.h"
#import "RCTViewManager.h"
#import "UIView+React.h"
Expand Down Expand Up @@ -225,7 +226,6 @@ @implementation RCTUIManager
NSDictionary *_componentDataByName;

NSMutableSet<id<RCTComponent>> *_bridgeTransactionListeners;
NSMutableSet<id<RCTUIManagerObserver>> *_uiManagerObservers;
}

@synthesize bridge = _bridge;
Expand Down Expand Up @@ -306,7 +306,7 @@ - (void)setBridge:(RCTBridge *)bridge
_rootViewTags = [NSMutableSet new];

_bridgeTransactionListeners = [NSMutableSet new];
_uiManagerObservers = [NSMutableSet new];
_observerCoordinator = [RCTUIManagerObserverCoordinator new];

_viewsToBeDeleted = [NSMutableSet new];

Expand Down Expand Up @@ -723,20 +723,6 @@ - (void)_amendPendingUIBlocksWithStylePropagationUpdateForShadowView:(RCTShadowV
}
}

- (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.
Expand Down Expand Up @@ -1159,10 +1145,19 @@ - (void)_layoutAndMount
[self addUIBlock:uiBlock];
}

[_observerCoordinator uiManagerWillPerformLayout:self];

// Perform layout
for (NSNumber *reactTag in _rootViewTags) {
RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
}

[_observerCoordinator uiManagerDidPerformLayout:self];

// Properies propagation
for (NSNumber *reactTag in _rootViewTags) {
RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
[self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:rootView];
}

Expand All @@ -1175,9 +1170,7 @@ - (void)_layoutAndMount
}
}];

for (id<RCTUIManagerObserver> observer in _uiManagerObservers) {
[observer uiManagerWillFlushUIBlocks:self];
}
[_observerCoordinator uiManagerWillFlushUIBlocks:self];

[self flushUIBlocks];
}
Expand Down
67 changes: 67 additions & 0 deletions React/Modules/RCTUIManagerObserverCoordinator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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/RCTViewManager.h>

/**
* Allows to hook into UIManager internals. This can be used to execute code at
* specific points during the view updating process.
* All observer handler is called on UIManager queue.
*/
@protocol RCTUIManagerObserver <NSObject>

@optional

/**
* Called just before the UIManager layout views.
* It allows performing some operation for components which contain custom
* layout logic right before regular Yoga based layout. So, for instance,
* some components which have own React-independent state can compute and cache
* own intrinsic content size (which will be used by Yoga) at this point.
*/
- (void)uiManagerWillPerformLayout:(RCTUIManager *)manager;

/**
* Called just after the UIManager layout views.
* It allows performing custom layout logic right after regular Yoga based layout.
* So, for instance, this can be used for computing final layout for a component,
* since it has its final frame set by Yoga at this point.
*/
- (void)uiManagerDidPerformLayout:(RCTUIManager *)manager;

/**
* 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

/**
* Simple helper which take care of RCTUIManager's observers.
*/
@interface RCTUIManagerObserverCoordinator : NSObject <RCTUIManagerObserver>

/**
* Add a UIManagerObserver. See the `RCTUIManagerObserver` protocol for more info.
* References to observers are held weakly.
* This method can be called safely from any queue.
*/
- (void)addObserver:(id<RCTUIManagerObserver>)observer;

/**
* Remove a `UIManagerObserver`.
* This method can be called safely from any queue.
*/
- (void)removeObserver:(id<RCTUIManagerObserver>)observer;

@end
70 changes: 70 additions & 0 deletions React/Modules/RCTUIManagerObserverCoordinator.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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 "RCTUIManagerObserverCoordinator.h"

#import "RCTUIManager.h"

@implementation RCTUIManagerObserverCoordinator {
NSHashTable<id<RCTUIManagerObserver>> *_observers;
}

- (instancetype)init
{
if (self = [super init]) {
_observers = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:0];
}

return self;
}

- (void)addObserver:(id<RCTUIManagerObserver>)observer
{
dispatch_async(RCTGetUIManagerQueue(), ^{
[self->_observers addObject:observer];
});
}

- (void)removeObserver:(id<RCTUIManagerObserver>)observer
{
dispatch_async(RCTGetUIManagerQueue(), ^{
[self->_observers removeObject:observer];
});
}

#pragma mark - RCTUIManagerObserver

- (void)uiManagerWillPerformLayout:(RCTUIManager *)manager
{
for (id<RCTUIManagerObserver> observer in _observers) {
if ([observer respondsToSelector:@selector(uiManagerWillPerformLayout:)]) {
[observer uiManagerWillPerformLayout:manager];
}
}
}

- (void)uiManagerDidPerformLayout:(RCTUIManager *)manager
{
for (id<RCTUIManagerObserver> observer in _observers) {
if ([observer respondsToSelector:@selector(uiManagerDidPerformLayout:)]) {
[observer uiManagerDidPerformLayout:manager];
}
}
}

- (void)uiManagerWillFlushUIBlocks:(RCTUIManager *)manager
{
for (id<RCTUIManagerObserver> observer in _observers) {
if ([observer respondsToSelector:@selector(uiManagerWillFlushUIBlocks:)]) {
[observer uiManagerWillFlushUIBlocks:manager];
}
}
}

@end
Loading

0 comments on commit 712b1dd

Please sign in to comment.