2015-03-23 20:28:42 +00:00
/ * *
* 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 .
* /
2015-02-20 04:10:52 +00:00
# import "RCTUIManager.h"
2015-03-10 21:23:03 +00:00
# import < AVFoundation / AVFoundation . h >
2016-12-07 13:12:11 +00:00
# import < yoga / Yoga . h >
2016-09-06 16:12:42 +00:00
2015-07-31 14:37:12 +00:00
# import "RCTAccessibilityManager.h"
2015-02-20 04:10:52 +00:00
# import "RCTAnimationType.h"
# import "RCTAssert.h"
2016-01-06 13:57:25 +00:00
# import "RCTBridge+Private.h"
2016-11-23 15:47:52 +00:00
# import "RCTBridge.h"
2015-08-06 22:44:15 +00:00
# import "RCTComponent.h"
# import "RCTComponentData.h"
2015-02-20 04:10:52 +00:00
# import "RCTConvert.h"
2015-04-21 12:26:51 +00:00
# import "RCTDefines.h"
[ReactNative] Introduce onLayout events
Summary:
Simply add an `onLayout` callback to a native view component, and the callback
will be invoked with the current layout information when the view is mounted and
whenever the layout changes.
The only limitation is that scroll position and other stuff the layout system
isn't aware of is not taken into account. This is because onLayout events
wouldn't be triggered for these changes and if they are desired they should be
tracked separately (e.g. with `onScroll`) and combined.
Also fixes some bugs with LayoutAnimation callbacks.
@public
Test Plan:
- Run new LayoutEventsExample in UIExplorer and see it work correctly.
- New integration test passes internally (IntegrationTest project seems busted).
- New jest test case passes.
{F22318433}
```
2015-05-06 15:45:05.848 [info][tid:com.facebook.React.JavaScript] "Running application "UIExplorerApp" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF"
2015-05-06 15:45:05.881 [info][tid:com.facebook.React.JavaScript] "received text layout event
", {"target":27,"layout":{"y":123,"x":12.5,"width":140.5,"height":18}}
2015-05-06 15:45:05.882 [info][tid:com.facebook.React.JavaScript] "received image layout event
", {"target":23,"layout":{"y":12.5,"x":122,"width":50,"height":50}}
2015-05-06 15:45:05.883 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":70.5,"x":20,"width":294,"height":204}}
2015-05-06 15:45:05.897 [info][tid:com.facebook.React.JavaScript] "received text layout event
", {"target":27,"layout":{"y":206.5,"x":12.5,"width":140.5,"height":18}}
2015-05-06 15:45:05.897 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":70.5,"x":20,"width":294,"height":287.5}}
2015-05-06 15:45:09.847 [info][tid:com.facebook.React.JavaScript] "layout animation done."
2015-05-06 15:45:09.847 [info][tid:com.facebook.React.JavaScript] "received image layout event
", {"target":23,"layout":{"y":12.5,"x":82,"width":50,"height":50}}
2015-05-06 15:45:09.848 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":110.5,"x":60,"width":214,"height":287.5}}
2015-05-06 15:45:09.862 [info][tid:com.facebook.React.JavaScript] "received text layout event
", {"target":27,"layout":{"y":206.5,"x":12.5,"width":120,"height":68}}
2015-05-06 15:45:09.863 [info][tid:com.facebook.React.JavaScript] "received image layout event
", {"target":23,"layout":{"y":12.5,"x":55,"width":50,"height":50}}
2015-05-06 15:45:09.863 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":128,"x":60,"width":160,"height":337.5}}
```
2015-05-07 19:11:02 +00:00
# import "RCTEventDispatcher.h"
2015-02-20 04:10:52 +00:00
# import "RCTLog.h"
2016-01-06 13:57:25 +00:00
# import "RCTModuleData.h"
# import "RCTModuleMethod.h"
2015-04-20 11:55:05 +00:00
# import "RCTProfile.h"
2017-03-09 02:45:28 +00:00
# import "RCTRootContentView.h"
2016-03-21 10:20:49 +00:00
# import "RCTRootShadowView.h"
2015-10-26 22:39:06 +00:00
# import "RCTRootViewInternal.h"
2015-02-20 04:10:52 +00:00
# import "RCTScrollableProtocol.h"
# import "RCTShadowView.h"
2017-05-08 19:40:07 +00:00
# import "RCTUIManagerObserverCoordinator.h"
2017-05-13 00:57:12 +00:00
# import "RCTUtils.h"
2015-02-20 04:10:52 +00:00
# import "RCTView.h"
# import "RCTViewManager.h"
2015-03-26 09:58:06 +00:00
# import "UIView+React.h"
2015-02-20 04:10:52 +00:00
2015-08-06 22:44:15 +00:00
static void RCTTraverseViewNodes ( id < RCTComponent > view , void ( ^ block ) ( id < RCTComponent > ) )
2015-07-17 10:53:15 +00:00
{
2015-11-11 16:12:13 +00:00
if ( view . reactTag ) {
block ( view ) ;
for ( id < RCTComponent > subview in view . reactSubviews ) {
RCTTraverseViewNodes ( subview , block ) ;
}
2015-07-17 10:53:15 +00:00
}
}
2015-02-20 04:10:52 +00:00
2016-05-04 13:54:12 +00:00
char * const RCTUIManagerQueueName = "com.facebook.react.ShadowQueue" ;
2015-07-31 14:37:12 +00:00
NSString * const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification = @ "RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification" ;
2015-08-17 13:11:29 +00:00
NSString * const RCTUIManagerDidRegisterRootViewNotification = @ "RCTUIManagerDidRegisterRootViewNotification" ;
NSString * const RCTUIManagerDidRemoveRootViewNotification = @ "RCTUIManagerDidRemoveRootViewNotification" ;
NSString * const RCTUIManagerRootViewKey = @ "RCTUIManagerRootViewKey" ;
2015-07-31 14:37:12 +00:00
2015-02-20 04:10:52 +00:00
@ interface RCTAnimation : NSObject
@ property ( nonatomic , readonly ) NSTimeInterval duration ;
@ property ( nonatomic , readonly ) NSTimeInterval delay ;
@ property ( nonatomic , readonly , copy ) NSString * property ;
@ property ( nonatomic , readonly ) CGFloat springDamping ;
@ property ( nonatomic , readonly ) CGFloat initialVelocity ;
@ property ( nonatomic , readonly ) RCTAnimationType animationType ;
@ end
2016-05-13 22:40:41 +00:00
static UIViewAnimationCurve _currentKeyboardAnimationCurve ;
2015-02-20 04:10:52 +00:00
@ implementation RCTAnimation
2015-06-25 16:14:19 +00:00
static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType ( RCTAnimationType type )
2015-02-20 04:10:52 +00:00
{
switch ( type ) {
case RCTAnimationTypeLinear :
2015-06-25 16:14:19 +00:00
return UIViewAnimationOptionCurveLinear ;
2015-02-20 04:10:52 +00:00
case RCTAnimationTypeEaseIn :
2015-06-25 16:14:19 +00:00
return UIViewAnimationOptionCurveEaseIn ;
2015-02-20 04:10:52 +00:00
case RCTAnimationTypeEaseOut :
2015-06-25 16:14:19 +00:00
return UIViewAnimationOptionCurveEaseOut ;
2015-02-20 04:10:52 +00:00
case RCTAnimationTypeEaseInEaseOut :
2015-06-25 16:14:19 +00:00
return UIViewAnimationOptionCurveEaseInOut ;
case RCTAnimationTypeKeyboard :
// http : // stackoverflow . com / questions / 18870447 / how - to - use - the - default - ios7 - uianimation - curve
2016-05-13 22:40:41 +00:00
return ( UIViewAnimationOptions ) ( _currentKeyboardAnimationCurve < < 16 ) ;
2015-02-20 04:10:52 +00:00
default :
2015-03-25 00:37:03 +00:00
RCTLogError ( @ "Unsupported animation type %zd" , type ) ;
2015-06-25 16:14:19 +00:00
return UIViewAnimationOptionCurveEaseInOut ;
2015-02-20 04:10:52 +00:00
}
}
2016-05-13 22:40:41 +00:00
// Use a custom initialization function rather than implementing ` + initialize` so that we can control
// when the initialization code runs . ` + initialize` runs immediately before the first message is sent
2016-05-16 15:01:35 +00:00
// to the class which may be too late for us . By this time , we may have missed some
2016-05-13 22:40:41 +00:00
// ` UIKeyboardWillChangeFrameNotification` s .
+ ( void ) initializeStatics
{
2016-09-27 13:19:45 +00:00
# if ! TARGET_OS _TV
2016-05-13 22:40:41 +00:00
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( keyboardWillChangeFrame : )
name : UIKeyboardWillChangeFrameNotification
object : nil ] ;
} ) ;
2016-09-27 13:19:45 +00:00
# endif
2016-05-13 22:40:41 +00:00
}
+ ( void ) keyboardWillChangeFrame : ( NSNotification * ) notification
{
2016-09-27 13:19:45 +00:00
# if ! TARGET_OS _TV
2016-05-13 22:40:41 +00:00
NSDictionary * userInfo = notification . userInfo ;
_currentKeyboardAnimationCurve = [ userInfo [ UIKeyboardAnimationCurveUserInfoKey ] integerValue ] ;
2016-09-27 13:19:45 +00:00
# endif
2016-05-13 22:40:41 +00:00
}
2015-02-20 04:10:52 +00:00
- ( instancetype ) initWithDuration : ( NSTimeInterval ) duration dictionary : ( NSDictionary * ) config
{
if ( ! config ) {
return nil ;
}
if ( ( self = [ super init ] ) ) {
_property = [ RCTConvert NSString : config [ @ "property" ] ] ;
2015-03-30 14:12:38 +00:00
_duration = [ RCTConvert NSTimeInterval : config [ @ "duration" ] ] ? : duration ;
if ( _duration > 0.0 && _duration < 0.01 ) {
RCTLogError ( @ "RCTLayoutAnimation expects timings to be in ms, not seconds." ) ;
_duration = _duration * 1000.0 ;
}
_delay = [ RCTConvert NSTimeInterval : config [ @ "delay" ] ] ;
if ( _delay > 0.0 && _delay < 0.01 ) {
RCTLogError ( @ "RCTLayoutAnimation expects timings to be in ms, not seconds." ) ;
_delay = _delay * 1000.0 ;
}
2015-02-20 04:10:52 +00:00
_animationType = [ RCTConvert RCTAnimationType : config [ @ "type" ] ] ;
if ( _animationType = = RCTAnimationTypeSpring ) {
_springDamping = [ RCTConvert CGFloat : config [ @ "springDamping" ] ] ;
_initialVelocity = [ RCTConvert CGFloat : config [ @ "initialVelocity" ] ] ;
}
}
return self ;
}
- ( void ) performAnimations : ( void ( ^ ) ( void ) ) animations
withCompletionBlock : ( void ( ^ ) ( BOOL completed ) ) completionBlock
{
if ( _animationType = = RCTAnimationTypeSpring ) {
[ UIView animateWithDuration : _duration
delay : _delay
usingSpringWithDamping : _springDamping
initialSpringVelocity : _initialVelocity
options : UIViewAnimationOptionBeginFromCurrentState
animations : animations
completion : completionBlock ] ;
} else {
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState |
2015-06-25 16:14:19 +00:00
UIViewAnimationOptionsFromRCTAnimationType ( _animationType ) ;
2015-02-20 04:10:52 +00:00
[ UIView animateWithDuration : _duration
delay : _delay
options : options
animations : animations
completion : completionBlock ] ;
}
}
@ end
@ interface RCTLayoutAnimation : NSObject
2015-07-07 21:14:14 +00:00
@ property ( nonatomic , copy ) NSDictionary * config ;
2015-02-20 04:10:52 +00:00
@ property ( nonatomic , strong ) RCTAnimation * createAnimation ;
@ property ( nonatomic , strong ) RCTAnimation * updateAnimation ;
@ property ( nonatomic , strong ) RCTAnimation * deleteAnimation ;
2015-07-07 21:14:14 +00:00
@ property ( nonatomic , copy ) RCTResponseSenderBlock callback ;
2015-02-20 04:10:52 +00:00
@ end
@ implementation RCTLayoutAnimation
- ( instancetype ) initWithDictionary : ( NSDictionary * ) config callback : ( RCTResponseSenderBlock ) callback
{
if ( ! config ) {
return nil ;
}
if ( ( self = [ super init ] ) ) {
2015-07-07 21:14:14 +00:00
_config = [ config copy ] ;
2015-03-30 14:12:38 +00:00
NSTimeInterval duration = [ RCTConvert NSTimeInterval : config [ @ "duration" ] ] ;
if ( duration > 0.0 && duration < 0.01 ) {
RCTLogError ( @ "RCTLayoutAnimation expects timings to be in ms, not seconds." ) ;
duration = duration * 1000.0 ;
}
2015-02-20 04:10:52 +00:00
_createAnimation = [ [ RCTAnimation alloc ] initWithDuration : duration dictionary : config [ @ "create" ] ] ;
_updateAnimation = [ [ RCTAnimation alloc ] initWithDuration : duration dictionary : config [ @ "update" ] ] ;
_deleteAnimation = [ [ RCTAnimation alloc ] initWithDuration : duration dictionary : config [ @ "delete" ] ] ;
_callback = callback ;
}
return self ;
}
@ end
@ implementation RCTUIManager
{
// Root views are only mutated on the shadow queue
2015-11-03 22:45:46 +00:00
NSMutableSet < NSNumber * > * _rootViewTags ;
2016-07-14 10:19:55 +00:00
NSMutableArray < RCTViewManagerUIBlock > * _pendingUIBlocks ;
2015-03-01 23:33:55 +00:00
2015-02-20 04:10:52 +00:00
// Animation
RCTLayoutAnimation * _layoutAnimation ; // Main thread only
2016-06-10 18:41:50 +00:00
NSMutableSet < UIView * > * _viewsToBeDeleted ; // Main thread only
2015-02-20 04:10:52 +00:00
2015-11-25 11:09:00 +00:00
NSMutableDictionary < NSNumber * , RCTShadowView * > * _shadowViewRegistry ; // RCT thread only
NSMutableDictionary < NSNumber * , UIView * > * _viewRegistry ; // Main thread only
2015-03-01 23:33:55 +00:00
// Keyed by viewName
2015-08-06 22:44:15 +00:00
NSDictionary * _componentDataByName ;
2015-06-01 10:01:55 +00:00
2015-11-03 22:45:46 +00:00
NSMutableSet < id < RCTComponent > > * _bridgeTransactionListeners ;
2015-02-20 04:10:52 +00:00
}
2015-04-02 14:33:21 +00:00
@ synthesize bridge = _bridge ;
2015-03-01 23:33:55 +00:00
2015-04-08 15:52:48 +00:00
RCT_EXPORT _MODULE ( )
2015-07-31 14:37:12 +00:00
- ( void ) didReceiveNewContentSizeMultiplier
{
2016-11-19 01:24:42 +00:00
// Report the event across the bridge .
2017-03-08 13:56:27 +00:00
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
2016-11-19 01:24:42 +00:00
[ _bridge . eventDispatcher sendDeviceEventWithName : @ "didUpdateContentSizeMultiplier"
body : @ ( [ _bridge . accessibilityManager multiplier ] ) ] ;
2017-03-08 13:56:27 +00:00
# pragma clang diagnostic pop
2016-11-19 01:24:42 +00:00
2016-05-16 15:01:35 +00:00
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
2016-07-13 15:01:44 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
object : self ] ;
2016-08-08 10:28:42 +00:00
[ self setNeedsLayout ] ;
2015-07-31 14:37:12 +00:00
} ) ;
}
2015-02-20 04:10:52 +00:00
- ( void ) invalidate
{
2015-05-04 17:35:49 +00:00
/ * *
* Called on the JS Thread since all modules are invalidated on the JS thread
* /
2015-02-20 04:10:52 +00:00
2015-11-03 11:54:23 +00:00
// This only accessed from the shadow queue
_pendingUIBlocks = nil ;
2015-05-04 17:35:49 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2016-05-14 00:15:05 +00:00
RCT_PROFILE _BEGIN _EVENT ( RCTProfileTagAlways , @ "UIManager invalidate" , nil ) ;
2016-07-07 19:36:56 +00:00
for ( NSNumber * rootViewTag in self -> _rootViewTags ) {
[ ( id < RCTInvalidating > ) self -> _viewRegistry [ rootViewTag ] invalidate ] ;
2015-05-04 17:35:49 +00:00
}
2015-04-11 22:08:00 +00:00
2016-07-07 19:36:56 +00:00
self -> _rootViewTags = nil ;
self -> _shadowViewRegistry = nil ;
self -> _viewRegistry = nil ;
self -> _bridgeTransactionListeners = nil ;
self -> _bridge = nil ;
2015-02-20 04:10:52 +00:00
2015-10-08 10:47:43 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
2016-09-05 18:11:37 +00:00
RCT_PROFILE _END _EVENT ( RCTProfileTagAlways , @ "" ) ;
2015-05-04 17:35:49 +00:00
} ) ;
2015-02-20 04:10:52 +00:00
}
2015-11-25 11:09:00 +00:00
- ( NSMutableDictionary < NSNumber * , RCTShadowView * > * ) shadowViewRegistry
{
// NOTE : this method only exists so that it can be accessed by unit tests
if ( ! _shadowViewRegistry ) {
_shadowViewRegistry = [ NSMutableDictionary new ] ;
}
return _shadowViewRegistry ;
}
- ( NSMutableDictionary < NSNumber * , UIView * > * ) viewRegistry
{
// NOTE : this method only exists so that it can be accessed by unit tests
if ( ! _viewRegistry ) {
_viewRegistry = [ NSMutableDictionary new ] ;
}
return _viewRegistry ;
}
2015-04-11 22:08:00 +00:00
- ( void ) setBridge : ( RCTBridge * ) bridge
{
RCTAssert ( _bridge = = nil , @ "Should not re-use same UIIManager instance" ) ;
_bridge = bridge ;
2015-11-25 11:09:00 +00:00
2015-11-14 18:25:00 +00:00
_shadowViewRegistry = [ NSMutableDictionary new ] ;
2015-11-25 11:09:00 +00:00
_viewRegistry = [ NSMutableDictionary new ] ;
// Internal resources
_pendingUIBlocks = [ NSMutableArray new ] ;
_rootViewTags = [ NSMutableSet new ] ;
_bridgeTransactionListeners = [ NSMutableSet new ] ;
2017-05-08 19:40:07 +00:00
_observerCoordinator = [ RCTUIManagerObserverCoordinator new ] ;
2015-04-11 22:08:00 +00:00
2016-06-10 18:41:50 +00:00
_viewsToBeDeleted = [ NSMutableSet new ] ;
2015-04-11 22:08:00 +00:00
// Get view managers from bridge
2015-08-17 14:35:34 +00:00
NSMutableDictionary * componentDataByName = [ NSMutableDictionary new ] ;
2015-11-25 11:09:00 +00:00
for ( Class moduleClass in _bridge . moduleClasses ) {
if ( [ moduleClass isSubclassOfClass : [ RCTViewManager class ] ] ) {
RCTComponentData * componentData = [ [ RCTComponentData alloc ] initWithManagerClass : moduleClass
bridge : _bridge ] ;
2015-08-06 22:44:15 +00:00
componentDataByName [ componentData . name ] = componentData ;
2015-04-11 22:08:00 +00:00
}
2015-08-06 22:44:15 +00:00
}
2015-04-11 22:08:00 +00:00
2015-08-06 22:44:15 +00:00
_componentDataByName = [ componentDataByName copy ] ;
2015-10-08 10:47:43 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( didReceiveNewContentSizeMultiplier )
name : RCTAccessibilityManagerDidUpdateMultiplierNotification
object : _bridge . accessibilityManager ] ;
2016-05-13 22:40:41 +00:00
[ RCTAnimation initializeStatics ] ;
2015-04-11 22:08:00 +00:00
}
2016-05-16 15:01:35 +00:00
dispatch_queue _t RCTGetUIManagerQueue ( void )
2015-04-18 17:43:20 +00:00
{
2016-05-16 15:01:35 +00:00
static dispatch_queue _t shadowQueue ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
2015-11-25 11:09:00 +00:00
if ( [ NSOperation instancesRespondToSelector : @ selector ( qualityOfService ) ] ) {
dispatch_queue _attr _t attr = dispatch_queue _attr _make _with _qos _class ( DISPATCH_QUEUE _SERIAL , QOS_CLASS _USER _INTERACTIVE , 0 ) ;
2016-05-16 15:01:35 +00:00
shadowQueue = dispatch_queue _create ( RCTUIManagerQueueName , attr ) ;
2015-11-25 11:09:00 +00:00
} else {
2016-05-16 15:01:35 +00:00
shadowQueue = dispatch_queue _create ( RCTUIManagerQueueName , DISPATCH_QUEUE _SERIAL ) ;
dispatch_set _target _queue ( shadowQueue , dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _HIGH , 0 ) ) ;
2015-11-25 11:09:00 +00:00
}
2016-05-16 15:01:35 +00:00
} ) ;
return shadowQueue ;
}
2017-05-08 19:40:06 +00:00
BOOL RCTIsUIManagerQueue ( )
{
static void * queueKey = & queueKey ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
dispatch_queue _set _specific ( RCTGetUIManagerQueue ( ) , queueKey , queueKey , NULL ) ;
} ) ;
return dispatch_get _specific ( queueKey ) = = queueKey ;
}
2016-05-16 15:01:35 +00:00
- ( dispatch_queue _t ) methodQueue
{
return RCTGetUIManagerQueue ( ) ;
2015-04-18 17:43:20 +00:00
}
2017-03-09 02:45:28 +00:00
- ( void ) registerRootView : ( RCTRootContentView * ) rootView
2015-02-20 04:10:52 +00:00
{
2016-06-06 14:57:55 +00:00
RCTAssertMainQueue ( ) ;
2015-03-01 23:33:55 +00:00
2015-02-20 04:10:52 +00:00
NSNumber * reactTag = rootView . reactTag ;
2015-03-25 00:37:03 +00:00
RCTAssert ( RCTIsReactRootView ( reactTag ) ,
@ "View %@ with tag #%@ is not a root view" , rootView , reactTag ) ;
2015-02-20 04:10:52 +00:00
UIView * existingView = _viewRegistry [ reactTag ] ;
2015-04-11 22:08:00 +00:00
RCTAssert ( existingView = = nil || existingView = = rootView ,
@ "Expect all root views to have unique tag. Added %@ twice" , reactTag ) ;
2015-03-01 23:33:55 +00:00
2017-03-09 02:45:28 +00:00
CGSize availableSize = rootView . availableSize ;
2015-02-20 04:10:52 +00:00
// Register view
_viewRegistry [ reactTag ] = rootView ;
2015-03-01 23:33:55 +00:00
2015-02-20 04:10:52 +00:00
// Register shadow view
2016-05-16 15:01:35 +00:00
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
2016-07-07 19:36:56 +00:00
if ( ! self -> _viewRegistry ) {
2015-06-06 21:12:37 +00:00
return ;
}
2016-07-13 15:01:44 +00:00
2016-03-21 10:20:49 +00:00
RCTRootShadowView * shadowView = [ RCTRootShadowView new ] ;
2017-03-09 02:45:28 +00:00
shadowView . availableSize = availableSize ;
2015-02-20 04:10:52 +00:00
shadowView . reactTag = reactTag ;
2015-05-26 11:14:31 +00:00
shadowView . backgroundColor = rootView . backgroundColor ;
shadowView . viewName = NSStringFromClass ( [ rootView class ] ) ;
2016-07-13 15:01:44 +00:00
self -> _shadowViewRegistry [ shadowView . reactTag ] = shadowView ;
[ self -> _rootViewTags addObject : reactTag ] ;
2015-02-20 04:10:52 +00:00
} ) ;
2015-08-17 13:11:29 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RCTUIManagerDidRegisterRootViewNotification
object : self
2015-12-15 13:42:45 +00:00
userInfo : @ { RCTUIManagerRootViewKey : rootView } ] ;
2015-02-20 04:10:52 +00:00
}
2017-01-27 02:14:40 +00:00
- ( NSString * ) viewNameForReactTag : ( NSNumber * ) reactTag
{
RCTAssertThread ( RCTGetUIManagerQueue ( ) , @ "viewNameForReactTag can only be called from the shadow queue" ) ;
return _shadowViewRegistry [ reactTag ] . viewName ;
}
2015-06-24 17:14:37 +00:00
- ( UIView * ) viewForReactTag : ( NSNumber * ) reactTag
{
2016-06-06 14:57:55 +00:00
RCTAssertMainQueue ( ) ;
2015-06-24 17:14:37 +00:00
return _viewRegistry [ reactTag ] ;
}
2017-05-16 16:39:44 +00:00
- ( RCTShadowView * ) shadowViewForReactTag : ( NSNumber * ) reactTag
{
RCTAssertUIManagerQueue ( ) ;
return _shadowViewRegistry [ reactTag ] ;
}
2017-02-20 07:05:42 +00:00
- ( void ) setAvailableSize : ( CGSize ) availableSize forRootView : ( UIView * ) rootView
2015-03-25 00:37:03 +00:00
{
2016-06-06 14:57:55 +00:00
RCTAssertMainQueue ( ) ;
2017-02-20 07:05:42 +00:00
NSNumber * reactTag = rootView . reactTag ;
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
RCTRootShadowView * shadowView = ( RCTRootShadowView * ) self -> _shadowViewRegistry [ reactTag ] ;
RCTAssert ( shadowView ! = nil , @ "Could not locate shadow view with tag #%@" , reactTag ) ;
RCTAssert ( [ shadowView isKindOfClass : [ RCTRootShadowView class ] ] , @ "Located shadow view (with tag #%@) is actually not root view." , reactTag ) ;
2015-03-25 00:37:03 +00:00
2017-02-20 07:05:42 +00:00
if ( CGSizeEqualToSize ( availableSize , shadowView . availableSize ) ) {
return ;
2015-10-27 16:20:48 +00:00
}
2017-02-20 07:05:42 +00:00
shadowView . availableSize = availableSize ;
[ self setNeedsLayout ] ;
} ) ;
}
- ( void ) setSize : ( CGSize ) size forView : ( UIView * ) view
{
RCTAssertMainQueue ( ) ;
2015-10-26 22:39:04 +00:00
2015-07-28 14:31:26 +00:00
NSNumber * reactTag = view . reactTag ;
2016-05-16 15:01:35 +00:00
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
2016-07-07 19:36:56 +00:00
RCTShadowView * shadowView = self -> _shadowViewRegistry [ reactTag ] ;
2016-02-25 17:38:23 +00:00
RCTAssert ( shadowView ! = nil , @ "Could not locate shadow view with tag #%@" , reactTag ) ;
2015-10-26 22:39:04 +00:00
2017-02-20 07:05:42 +00:00
if ( CGSizeEqualToSize ( size , shadowView . size ) ) {
return ;
2016-02-25 17:38:23 +00:00
}
2016-08-08 10:28:42 +00:00
2017-02-20 07:05:42 +00:00
shadowView . size = size ;
[ self setNeedsLayout ] ;
2015-03-25 00:37:03 +00:00
} ) ;
}
2016-03-01 18:13:22 +00:00
- ( void ) setIntrinsicContentSize : ( CGSize ) size forView : ( UIView * ) view
{
2016-06-06 14:57:55 +00:00
RCTAssertMainQueue ( ) ;
2016-03-01 18:13:22 +00:00
NSNumber * reactTag = view . reactTag ;
2016-05-16 15:01:35 +00:00
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
2016-07-07 19:36:56 +00:00
RCTShadowView * shadowView = self -> _shadowViewRegistry [ reactTag ] ;
2017-02-01 00:47:11 +00:00
RCTAssert ( shadowView ! = nil , @ "Could not locate view with tag #%@" , reactTag ) ;
2016-03-01 18:13:22 +00:00
2017-02-01 00:47:11 +00:00
if ( ! CGSizeEqualToSize ( shadowView . intrinsicContentSize , size ) ) {
shadowView . intrinsicContentSize = size ;
[ self setNeedsLayout ] ;
}
2016-03-01 18:13:22 +00:00
} ) ;
}
2016-03-21 10:20:49 +00:00
- ( void ) setBackgroundColor : ( UIColor * ) color forView : ( UIView * ) view
2015-05-26 11:14:31 +00:00
{
2016-06-06 14:57:55 +00:00
RCTAssertMainQueue ( ) ;
2015-05-26 11:14:31 +00:00
2016-03-21 10:20:49 +00:00
NSNumber * reactTag = view . reactTag ;
2016-05-16 15:01:35 +00:00
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
2016-07-07 19:36:56 +00:00
if ( ! self -> _viewRegistry ) {
2015-06-06 20:37:51 +00:00
return ;
}
2016-07-13 15:01:44 +00:00
RCTShadowView * shadowView = self -> _shadowViewRegistry [ reactTag ] ;
2016-03-21 10:20:49 +00:00
RCTAssert ( shadowView ! = nil , @ "Could not locate root view with tag #%@" , reactTag ) ;
shadowView . backgroundColor = color ;
[ self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView : shadowView ] ;
2015-05-26 11:14:31 +00:00
[ self flushUIBlocks ] ;
} ) ;
}
2015-02-20 04:10:52 +00:00
/ * *
* Unregisters views from registries
* /
2015-11-03 22:45:46 +00:00
- ( void ) _purgeChildren : ( NSArray < id < RCTComponent > > * ) children
2015-11-14 18:25:00 +00:00
fromRegistry : ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) registry
2015-02-20 04:10:52 +00:00
{
2015-08-06 22:44:15 +00:00
for ( id < RCTComponent > child in children ) {
RCTTraverseViewNodes ( registry [ child . reactTag ] , ^ ( id < RCTComponent > subview ) {
2015-02-20 04:10:52 +00:00
RCTAssert ( ! [ subview isReactRootView ] , @ "Root views should not be unregistered" ) ;
if ( [ subview conformsToProtocol : @ protocol ( RCTInvalidating ) ] ) {
[ ( id < RCTInvalidating > ) subview invalidate ] ;
}
2015-11-14 18:25:00 +00:00
[ registry removeObjectForKey : subview . reactTag ] ;
2015-06-01 10:01:55 +00:00
2016-07-07 19:36:56 +00:00
if ( registry = = ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) self -> _viewRegistry ) {
[ self -> _bridgeTransactionListeners removeObject : subview ] ;
2015-06-01 10:01:55 +00:00
}
2015-02-20 04:10:52 +00:00
} ) ;
}
}
- ( void ) addUIBlock : ( RCTViewManagerUIBlock ) block
{
2016-05-16 15:01:35 +00:00
RCTAssertThread ( RCTGetUIManagerQueue ( ) ,
2015-05-25 12:19:53 +00:00
@ "-[RCTUIManager addUIBlock:] should only be called from the "
2016-05-16 15:01:35 +00:00
"UIManager's queue (get this using `RCTGetUIManagerQueue()`)" ) ;
2015-05-25 12:19:53 +00:00
2016-07-13 15:01:44 +00:00
if ( ! block || ! _viewRegistry ) {
2015-05-27 01:39:37 +00:00
return ;
}
2016-07-14 10:19:55 +00:00
[ _pendingUIBlocks addObject : block ] ;
2015-02-20 04:10:52 +00:00
}
2017-03-28 12:30:00 +00:00
- ( 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 ] ;
}
2016-03-21 10:20:49 +00:00
- ( RCTViewManagerUIBlock ) uiBlockWithLayoutUpdateForRootView : ( RCTRootShadowView * ) rootShadowView
2015-02-20 04:10:52 +00:00
{
2016-06-06 14:57:55 +00:00
RCTAssert ( ! RCTIsMainQueue ( ) , @ "Should be called on shadow queue" ) ;
2015-03-01 23:33:55 +00:00
2015-02-20 04:10:52 +00:00
// This is nuanced . In the JS thread , we create a new update buffer
// ` frameTags` / ` frames` that is created / mutated in the JS thread . We access
// these structures in the UI - thread block . ` NSMutableArray` is not thread
// safe so we rely on the fact that we never mutate it after it ' s passed to
// the main thread .
2016-03-21 10:20:49 +00:00
NSSet < RCTShadowView * > * viewsWithNewFrames = [ rootShadowView collectViewsWithUpdatedFrames ] ;
2015-11-17 14:35:46 +00:00
if ( ! viewsWithNewFrames . count ) {
// no frame change results in no UI update block
return nil ;
}
2015-02-20 04:10:52 +00:00
2016-05-31 15:38:47 +00:00
typedef struct {
CGRect frame ;
2017-02-02 17:51:19 +00:00
UIUserInterfaceLayoutDirection layoutDirection ;
2016-05-31 15:38:47 +00:00
BOOL isNew ;
BOOL parentIsNew ;
BOOL isHidden ;
} RCTFrameData ;
// Construct arrays then hand off to main thread
2016-07-12 12:51:56 +00:00
NSUInteger count = viewsWithNewFrames . count ;
2016-05-31 15:38:47 +00:00
NSMutableArray * reactTags = [ [ NSMutableArray alloc ] initWithCapacity : count ] ;
NSMutableData * framesData = [ [ NSMutableData alloc ] initWithLength : sizeof ( RCTFrameData ) * count ] ;
{
2016-07-12 12:51:56 +00:00
NSUInteger index = 0 ;
2016-05-31 15:38:47 +00:00
RCTFrameData * frameDataArray = ( RCTFrameData * ) framesData . mutableBytes ;
for ( RCTShadowView * shadowView in viewsWithNewFrames ) {
reactTags [ index ] = shadowView . reactTag ;
frameDataArray [ index + + ] = ( RCTFrameData ) {
shadowView . frame ,
2017-02-02 17:51:19 +00:00
shadowView . effectiveLayoutDirection ,
2016-05-31 15:38:47 +00:00
shadowView . isNewView ,
shadowView . superview . isNewView ,
shadowView . isHidden ,
} ;
}
2015-02-20 04:10:52 +00:00
}
2015-06-01 15:34:09 +00:00
// These are blocks to be executed on each view , immediately after
// reactSetFrame : has been called . Note that if reactSetFrame : is not called ,
// these won ' t be called either , so this is not a suitable place to update
// properties that aren ' t related to layout .
2016-05-31 15:38:47 +00:00
NSMutableDictionary < NSNumber * , RCTViewManagerUIBlock > * updateBlocks =
[ NSMutableDictionary new ] ;
2015-03-01 23:33:55 +00:00
for ( RCTShadowView * shadowView in viewsWithNewFrames ) {
2016-05-31 15:38:47 +00:00
// We have to do this after we build the parentsAreNew array .
shadowView . newView = NO ;
NSNumber * reactTag = shadowView . reactTag ;
2015-08-06 22:44:15 +00:00
RCTViewManager * manager = [ _componentDataByName [ shadowView . viewName ] manager ] ;
2015-03-01 23:33:55 +00:00
RCTViewManagerUIBlock block = [ manager uiBlockToAmendWithShadowView : shadowView ] ;
2016-02-01 15:50:50 +00:00
if ( block ) {
2016-05-31 15:38:47 +00:00
updateBlocks [ reactTag ] = block ;
2016-02-01 15:50:50 +00:00
}
2015-09-03 10:36:32 +00:00
if ( shadowView . onLayout ) {
CGRect frame = shadowView . frame ;
shadowView . onLayout ( @ {
@ "layout" : @ {
@ "x" : @ ( frame . origin . x ) ,
@ "y" : @ ( frame . origin . y ) ,
@ "width" : @ ( frame . size . width ) ,
@ "height" : @ ( frame . size . height ) ,
} ,
} ) ;
}
2015-10-26 22:39:06 +00:00
2016-05-31 15:38:47 +00:00
if ( RCTIsReactRootView ( reactTag ) ) {
2015-10-26 22:39:06 +00:00
CGSize contentSize = shadowView . frame . size ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2016-07-07 19:36:56 +00:00
UIView * view = self -> _viewRegistry [ reactTag ] ;
2015-10-26 22:39:06 +00:00
RCTAssert ( view ! = nil , @ "view (for ID %@) not found" , reactTag ) ;
RCTRootView * rootView = ( RCTRootView * ) [ view superview ] ;
2017-02-27 18:47:30 +00:00
rootView . intrinsicContentSize = contentSize ;
2015-10-26 22:39:06 +00:00
} ) ;
}
2015-03-01 23:33:55 +00:00
}
2015-02-20 04:10:52 +00:00
// Perform layout ( possibly animated )
2015-11-14 18:25:00 +00:00
return ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2016-05-31 15:38:47 +00:00
const RCTFrameData * frameDataArray = ( const RCTFrameData * ) framesData . bytes ;
RCTLayoutAnimation * layoutAnimation = uiManager -> _layoutAnimation ;
2016-05-28 19:12:25 +00:00
__block NSUInteger completionsCalled = 0 ;
2016-05-31 15:38:47 +00:00
NSInteger index = 0 ;
for ( NSNumber * reactTag in reactTags ) {
RCTFrameData frameData = frameDataArray [ index + + ] ;
2016-05-28 19:12:25 +00:00
UIView * view = viewRegistry [ reactTag ] ;
2016-05-31 15:38:47 +00:00
CGRect frame = frameData . frame ;
2016-05-28 19:12:25 +00:00
2016-05-31 15:38:47 +00:00
BOOL isHidden = frameData . isHidden ;
2017-02-02 17:51:19 +00:00
UIUserInterfaceLayoutDirection layoutDirection = frameData . layoutDirection ;
2016-05-31 15:38:47 +00:00
BOOL isNew = frameData . isNew ;
2016-05-28 19:12:25 +00:00
RCTAnimation * updateAnimation = isNew ? nil : layoutAnimation . updateAnimation ;
2016-05-31 15:38:47 +00:00
BOOL shouldAnimateCreation = isNew && ! frameData . parentIsNew ;
2016-05-28 19:12:25 +00:00
RCTAnimation * createAnimation = shouldAnimateCreation ? layoutAnimation . createAnimation : nil ;
void ( ^ completion ) ( BOOL ) = ^ ( BOOL finished ) {
completionsCalled + + ;
2016-05-31 15:38:47 +00:00
if ( layoutAnimation . callback && completionsCalled = = count ) {
2016-05-28 19:12:25 +00:00
layoutAnimation . callback ( @ [ @ ( finished ) ] ) ;
// It ' s unsafe to call this callback more than once , so we nil it out here
// to make sure that doesn ' t happen .
layoutAnimation . callback = nil ;
2015-02-20 04:10:52 +00:00
}
2016-05-28 19:12:25 +00:00
} ;
2016-05-27 11:46:25 +00:00
2016-05-28 19:12:25 +00:00
if ( view . isHidden ! = isHidden ) {
view . hidden = isHidden ;
}
2016-05-27 11:46:25 +00:00
2017-02-02 17:51:19 +00:00
if ( view . reactLayoutDirection ! = layoutDirection ) {
view . reactLayoutDirection = layoutDirection ;
}
2016-05-31 15:38:47 +00:00
RCTViewManagerUIBlock updateBlock = updateBlocks [ reactTag ] ;
2016-05-28 19:12:25 +00:00
if ( createAnimation ) {
2016-05-31 15:38:47 +00:00
// Animate view creation
2016-05-28 19:12:25 +00:00
[ view reactSetFrame : frame ] ;
2016-05-27 11:46:25 +00:00
2016-05-28 19:12:25 +00:00
CATransform3D finalTransform = view . layer . transform ;
CGFloat finalOpacity = view . layer . opacity ;
2016-05-31 15:38:47 +00:00
NSString * property = createAnimation . property ;
if ( [ property isEqualToString : @ "scaleXY" ] ) {
2016-05-28 19:12:25 +00:00
view . layer . transform = CATransform3DMakeScale ( 0 , 0 , 0 ) ;
2016-05-31 15:38:47 +00:00
} else if ( [ property isEqualToString : @ "opacity" ] ) {
2016-05-28 19:12:25 +00:00
view . layer . opacity = 0.0 ;
2016-05-31 15:38:47 +00:00
} else {
RCTLogError ( @ "Unsupported layout animation createConfig property %@" ,
createAnimation . property ) ;
2016-05-27 11:46:25 +00:00
}
2016-05-28 19:12:25 +00:00
[ createAnimation performAnimations : ^ {
2016-05-31 15:38:47 +00:00
if ( [ property isEqualToString : @ "scaleXY" ] ) {
2016-05-28 19:12:25 +00:00
view . layer . transform = finalTransform ;
2016-05-31 15:38:47 +00:00
} else if ( [ property isEqualToString : @ "opacity" ] ) {
2016-05-28 19:12:25 +00:00
view . layer . opacity = finalOpacity ;
2016-02-01 15:50:50 +00:00
}
2016-05-28 19:12:25 +00:00
if ( updateBlock ) {
2016-05-31 15:38:47 +00:00
updateBlock ( self , viewRegistry ) ;
2016-05-28 19:12:25 +00:00
}
} withCompletionBlock : completion ] ;
2016-02-01 15:50:50 +00:00
2016-05-28 19:12:25 +00:00
} else if ( updateAnimation ) {
2016-05-31 15:38:47 +00:00
// Animate view update
2016-05-28 19:12:25 +00:00
[ updateAnimation performAnimations : ^ {
2016-02-01 15:50:50 +00:00
[ view reactSetFrame : frame ] ;
2016-05-28 19:12:25 +00:00
if ( updateBlock ) {
2016-05-31 15:38:47 +00:00
updateBlock ( self , viewRegistry ) ;
2016-05-27 11:46:25 +00:00
}
2016-05-28 19:12:25 +00:00
} withCompletionBlock : completion ] ;
} else {
2016-02-01 15:50:50 +00:00
2016-05-31 15:38:47 +00:00
// Update without animation
[ view reactSetFrame : frame ] ;
2016-05-28 19:12:25 +00:00
if ( updateBlock ) {
2016-05-31 15:38:47 +00:00
updateBlock ( self , viewRegistry ) ;
2016-02-01 15:50:50 +00:00
}
2016-05-28 19:12:25 +00:00
completion ( YES ) ;
2015-02-20 04:10:52 +00:00
}
}
2016-04-25 07:08:42 +00:00
2016-05-31 15:38:47 +00:00
// Clean up
uiManager -> _layoutAnimation = nil ;
2015-02-20 04:10:52 +00:00
} ;
}
2016-03-21 10:20:49 +00:00
- ( void ) _amendPendingUIBlocksWithStylePropagationUpdateForShadowView : ( RCTShadowView * ) topView
2015-02-20 04:10:52 +00:00
{
2015-11-03 22:45:46 +00:00
NSMutableSet < RCTApplierBlock > * applierBlocks = [ NSMutableSet setWithCapacity : 1 ] ;
2015-02-20 04:10:52 +00:00
[ topView collectUpdatedProperties : applierBlocks parentProperties : @ { } ] ;
2015-03-01 23:33:55 +00:00
2015-10-27 12:07:44 +00:00
if ( applierBlocks . count ) {
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-10-27 12:07:44 +00:00
for ( RCTApplierBlock block in applierBlocks ) {
block ( viewRegistry ) ;
}
} ] ;
}
2015-02-20 04:10:52 +00:00
}
/ * *
* A method to be called from JS , which takes a container ID and then releases
* all subviews for that container upon receipt .
* /
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( removeSubviewsFromContainerWithID : ( nonnull NSNumber * ) containerID )
2015-02-20 04:10:52 +00:00
{
2015-08-06 22:44:15 +00:00
id < RCTComponent > container = _shadowViewRegistry [ containerID ] ;
2015-02-20 04:10:52 +00:00
RCTAssert ( container ! = nil , @ "container view (for ID %@) not found" , containerID ) ;
2015-03-01 23:33:55 +00:00
2015-06-15 14:53:45 +00:00
NSUInteger subviewsCount = [ container reactSubviews ] . count ;
2015-11-03 22:45:46 +00:00
NSMutableArray < NSNumber * > * indices = [ [ NSMutableArray alloc ] initWithCapacity : subviewsCount ] ;
2015-06-15 14:53:45 +00:00
for ( NSUInteger childIndex = 0 ; childIndex < subviewsCount ; childIndex + + ) {
2015-02-20 04:10:52 +00:00
[ indices addObject : @ ( childIndex ) ] ;
}
[ self manageChildren : containerID
moveFromIndices : nil
moveToIndices : nil
addChildReactTags : nil
addAtIndices : nil
removeAtIndices : indices ] ;
}
/ * *
* Disassociates children from container . Doesn ' t remove from registries .
* TODO : use [ NSArray getObjects : buffer ] to reuse same fast buffer each time .
*
* @ returns Array of removed items .
* /
2015-11-03 22:45:46 +00:00
- ( NSArray < id < RCTComponent > > * ) _childrenToRemoveFromContainer : ( id < RCTComponent > ) container
atIndices : ( NSArray < NSNumber * > * ) atIndices
2015-02-20 04:10:52 +00:00
{
// If there are no indices to move or the container has no subviews don ' t bother
// We support parents with nil subviews so long as they ' re all nil so this allows for this behavior
2015-06-15 14:53:45 +00:00
if ( atIndices . count = = 0 || [ container reactSubviews ] . count = = 0 ) {
2015-02-20 04:10:52 +00:00
return nil ;
}
// Construction of removed children must be done "up front" , before indices are disturbed by removals .
2015-11-03 22:45:46 +00:00
NSMutableArray < id < RCTComponent > > * removedChildren = [ NSMutableArray arrayWithCapacity : atIndices . count ] ;
2015-04-11 22:08:00 +00:00
RCTAssert ( container ! = nil , @ "container view (for ID %@) not found" , container ) ;
2015-06-15 14:53:45 +00:00
for ( NSNumber * indexNumber in atIndices ) {
NSUInteger index = indexNumber . unsignedIntegerValue ;
if ( index < [ container reactSubviews ] . count ) {
2015-02-20 04:10:52 +00:00
[ removedChildren addObject : [ container reactSubviews ] [ index ] ] ;
}
}
if ( removedChildren . count ! = atIndices . count ) {
2015-11-05 20:19:56 +00:00
NSString * message = [ NSString stringWithFormat : @ "removedChildren count (%tu) was not what we expected (%tu)" ,
removedChildren . count , atIndices . count ] ;
RCTFatal ( RCTErrorWithMessage ( message ) ) ;
2015-02-20 04:10:52 +00:00
}
return removedChildren ;
}
2015-11-03 22:45:46 +00:00
- ( void ) _removeChildren : ( NSArray < id < RCTComponent > > * ) children
fromContainer : ( id < RCTComponent > ) container
2015-02-20 04:10:52 +00:00
{
2016-06-10 18:41:50 +00:00
for ( id < RCTComponent > removedChild in children ) {
[ container removeReactSubview : removedChild ] ;
}
}
2016-04-25 07:08:42 +00:00
2016-06-10 18:41:50 +00:00
/ * *
* Remove subviews from their parent with an animation .
* /
- ( void ) _removeChildren : ( NSArray < UIView * > * ) children
fromContainer : ( UIView * ) container
withAnimation : ( RCTLayoutAnimation * ) animation
{
RCTAssertMainQueue ( ) ;
RCTAnimation * deleteAnimation = animation . deleteAnimation ;
2016-06-10 13:52:20 +00:00
2016-06-10 18:41:50 +00:00
__block NSUInteger completionsCalled = 0 ;
for ( UIView * removedChild in children ) {
2016-04-25 07:08:42 +00:00
void ( ^ completion ) ( BOOL ) = ^ ( BOOL finished ) {
completionsCalled + + ;
2016-07-07 19:36:56 +00:00
[ self -> _viewsToBeDeleted removeObject : removedChild ] ;
2016-04-25 07:08:42 +00:00
[ container removeReactSubview : removedChild ] ;
2016-06-10 18:41:50 +00:00
if ( animation . callback && completionsCalled = = children . count ) {
animation . callback ( @ [ @ ( finished ) ] ) ;
2016-04-25 07:08:42 +00:00
// It ' s unsafe to call this callback more than once , so we nil it out here
// to make sure that doesn ' t happen .
2016-06-10 18:41:50 +00:00
animation . callback = nil ;
2016-04-25 07:08:42 +00:00
}
} ;
2016-06-10 18:41:50 +00:00
[ _viewsToBeDeleted addObject : removedChild ] ;
2016-04-25 07:08:42 +00:00
2016-06-10 18:41:50 +00:00
// Disable user interaction while the view is animating since JS won ' t receive
// the view events anyway .
removedChild . userInteractionEnabled = NO ;
2016-04-25 07:08:42 +00:00
2016-06-10 18:41:50 +00:00
NSString * property = deleteAnimation . property ;
[ deleteAnimation performAnimations : ^ {
if ( [ property isEqualToString : @ "scaleXY" ] ) {
2016-11-23 19:44:48 +00:00
removedChild . layer . transform = CATransform3DMakeScale ( 0.001 , 0.001 , 0.001 ) ;
2016-06-10 18:41:50 +00:00
} else if ( [ property isEqualToString : @ "opacity" ] ) {
removedChild . layer . opacity = 0.0 ;
} else {
RCTLogError ( @ "Unsupported layout animation createConfig property %@" ,
deleteAnimation . property ) ;
}
} withCompletionBlock : completion ] ;
2015-02-20 04:10:52 +00:00
}
}
2016-06-10 18:41:50 +00:00
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( removeRootView : ( nonnull NSNumber * ) rootReactTag )
2015-02-20 04:10:52 +00:00
{
RCTShadowView * rootShadowView = _shadowViewRegistry [ rootReactTag ] ;
RCTAssert ( rootShadowView . superview = = nil , @ "root view cannot have superview (ID %@)" , rootReactTag ) ;
2015-11-14 18:25:00 +00:00
[ self _purgeChildren : ( NSArray < id < RCTComponent > > * ) rootShadowView . reactSubviews
fromRegistry : ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) _shadowViewRegistry ] ;
[ _shadowViewRegistry removeObjectForKey : rootReactTag ] ;
2015-02-20 04:10:52 +00:00
[ _rootViewTags removeObject : rootReactTag ] ;
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2016-06-06 14:57:55 +00:00
RCTAssertMainQueue ( ) ;
2015-02-20 04:10:52 +00:00
UIView * rootView = viewRegistry [ rootReactTag ] ;
2015-11-14 18:25:00 +00:00
[ uiManager _purgeChildren : ( NSArray < id < RCTComponent > > * ) rootView . reactSubviews
fromRegistry : ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) viewRegistry ] ;
2016-07-12 12:51:56 +00:00
[ ( NSMutableDictionary * ) viewRegistry removeObjectForKey : rootReactTag ] ;
2015-08-17 13:11:29 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RCTUIManagerDidRemoveRootViewNotification
object : uiManager
2015-12-15 13:42:45 +00:00
userInfo : @ { RCTUIManagerRootViewKey : rootView } ] ;
2015-02-20 04:10:52 +00:00
} ] ;
}
2015-11-03 22:45:46 +00:00
RCT_EXPORT _METHOD ( replaceExistingNonRootView : ( nonnull NSNumber * ) reactTag
withView : ( nonnull NSNumber * ) newReactTag )
2015-02-20 04:10:52 +00:00
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
RCTAssert ( shadowView ! = nil , @ "shadowView (for ID %@) not found" , reactTag ) ;
RCTShadowView * superShadowView = shadowView . superview ;
2016-10-27 11:17:43 +00:00
if ( ! superShadowView ) {
RCTAssert ( NO , @ "shadowView super (of ID %@) not found" , reactTag ) ;
return ;
}
2015-02-20 04:10:52 +00:00
NSUInteger indexOfView = [ superShadowView . reactSubviews indexOfObject : shadowView ] ;
RCTAssert ( indexOfView ! = NSNotFound , @ "View's superview doesn't claim it as subview (id %@)" , reactTag ) ;
2015-11-03 22:45:46 +00:00
NSArray < NSNumber * > * removeAtIndices = @ [ @ ( indexOfView ) ] ;
NSArray < NSNumber * > * addTags = @ [ newReactTag ] ;
2015-02-20 04:10:52 +00:00
[ self manageChildren : superShadowView . reactTag
2016-10-27 11:17:43 +00:00
moveFromIndices : nil
moveToIndices : nil
addChildReactTags : addTags
2015-02-20 04:10:52 +00:00
addAtIndices : removeAtIndices
2016-10-27 11:17:43 +00:00
removeAtIndices : removeAtIndices ] ;
2015-02-20 04:10:52 +00:00
}
2015-12-15 14:56:07 +00:00
RCT_EXPORT _METHOD ( setChildren : ( nonnull NSNumber * ) containerTag
reactTags : ( NSArray < NSNumber * > * ) reactTags )
{
RCTSetChildren ( containerTag , reactTags ,
( NSDictionary < NSNumber * , id < RCTComponent > > * ) _shadowViewRegistry ) ;
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
RCTSetChildren ( containerTag , reactTags ,
( NSDictionary < NSNumber * , id < RCTComponent > > * ) viewRegistry ) ;
} ] ;
}
static void RCTSetChildren ( NSNumber * containerTag ,
NSArray < NSNumber * > * reactTags ,
NSDictionary < NSNumber * , id < RCTComponent > > * registry )
{
id < RCTComponent > container = registry [ containerTag ] ;
NSInteger index = 0 ;
for ( NSNumber * reactTag in reactTags ) {
id < RCTComponent > view = registry [ reactTag ] ;
if ( view ) {
[ container insertReactSubview : view atIndex : index + + ] ;
}
}
}
2016-06-07 07:08:16 +00:00
RCT_EXPORT _METHOD ( manageChildren : ( nonnull NSNumber * ) containerTag
2015-12-10 18:09:04 +00:00
moveFromIndices : ( NSArray < NSNumber * > * ) moveFromIndices
moveToIndices : ( NSArray < NSNumber * > * ) moveToIndices
addChildReactTags : ( NSArray < NSNumber * > * ) addChildReactTags
addAtIndices : ( NSArray < NSNumber * > * ) addAtIndices
removeAtIndices : ( NSArray < NSNumber * > * ) removeAtIndices )
2015-02-20 04:10:52 +00:00
{
2016-06-07 07:08:16 +00:00
[ self _manageChildren : containerTag
2015-07-17 10:53:15 +00:00
moveFromIndices : moveFromIndices
moveToIndices : moveToIndices
2015-02-20 04:10:52 +00:00
addChildReactTags : addChildReactTags
addAtIndices : addAtIndices
removeAtIndices : removeAtIndices
2015-11-14 18:25:00 +00:00
registry : ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) _shadowViewRegistry ] ;
2015-02-20 04:10:52 +00:00
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2016-06-07 07:08:16 +00:00
[ uiManager _manageChildren : containerTag
2015-07-17 10:53:15 +00:00
moveFromIndices : moveFromIndices
moveToIndices : moveToIndices
addChildReactTags : addChildReactTags
addAtIndices : addAtIndices
removeAtIndices : removeAtIndices
2015-11-14 18:25:00 +00:00
registry : ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) viewRegistry ] ;
2015-07-17 10:53:15 +00:00
} ] ;
2015-02-20 04:10:52 +00:00
}
2016-06-07 07:08:16 +00:00
- ( void ) _manageChildren : ( NSNumber * ) containerTag
2015-11-03 22:45:46 +00:00
moveFromIndices : ( NSArray < NSNumber * > * ) moveFromIndices
moveToIndices : ( NSArray < NSNumber * > * ) moveToIndices
addChildReactTags : ( NSArray < NSNumber * > * ) addChildReactTags
addAtIndices : ( NSArray < NSNumber * > * ) addAtIndices
removeAtIndices : ( NSArray < NSNumber * > * ) removeAtIndices
2015-11-14 18:25:00 +00:00
registry : ( NSMutableDictionary < NSNumber * , id < RCTComponent > > * ) registry
2015-02-20 04:10:52 +00:00
{
2016-06-07 07:08:16 +00:00
id < RCTComponent > container = registry [ containerTag ] ;
2015-12-04 00:04:34 +00:00
RCTAssert ( moveFromIndices . count = = moveToIndices . count , @ "moveFromIndices had size %tu, moveToIndices had size %tu" , moveFromIndices . count , moveToIndices . count ) ;
RCTAssert ( addChildReactTags . count = = addAtIndices . count , @ "there should be at least one React child to add" ) ;
2015-02-20 04:10:52 +00:00
2015-12-04 00:04:34 +00:00
// Removes ( both permanent and temporary moves ) are using "before" indices
NSArray < id < RCTComponent > > * permanentlyRemovedChildren =
[ self _childrenToRemoveFromContainer : container atIndices : removeAtIndices ] ;
NSArray < id < RCTComponent > > * temporarilyRemovedChildren =
[ self _childrenToRemoveFromContainer : container atIndices : moveFromIndices ] ;
2015-02-20 04:10:52 +00:00
2016-06-10 18:41:50 +00:00
BOOL isUIViewRegistry = ( ( id ) registry = = ( id ) _viewRegistry ) ;
if ( isUIViewRegistry && _layoutAnimation . deleteAnimation ) {
[ self _removeChildren : ( NSArray < UIView * > * ) permanentlyRemovedChildren
fromContainer : ( UIView * ) container
withAnimation : _layoutAnimation ] ;
} else {
[ self _removeChildren : permanentlyRemovedChildren fromContainer : container ] ;
}
2015-12-03 18:33:03 +00:00
2016-06-10 18:41:50 +00:00
[ self _removeChildren : temporarilyRemovedChildren fromContainer : container ] ;
[ self _purgeChildren : permanentlyRemovedChildren fromRegistry : registry ] ;
2016-06-10 13:52:20 +00:00
2015-12-04 00:04:34 +00:00
// Figure out what to insert - merge temporary inserts and adds
NSMutableDictionary * destinationsToChildrenToAdd = [ NSMutableDictionary dictionary ] ;
for ( NSInteger index = 0 , length = temporarilyRemovedChildren . count ; index < length ; index + + ) {
destinationsToChildrenToAdd [ moveToIndices [ index ] ] = temporarilyRemovedChildren [ index ] ;
}
for ( NSInteger index = 0 , length = addAtIndices . count ; index < length ; index + + ) {
id < RCTComponent > view = registry [ addChildReactTags [ index ] ] ;
if ( view ) {
destinationsToChildrenToAdd [ addAtIndices [ index ] ] = view ;
}
2015-02-20 04:10:52 +00:00
}
2015-12-04 00:04:34 +00:00
NSArray < NSNumber * > * sortedIndices =
[ destinationsToChildrenToAdd . allKeys sortedArrayUsingSelector : @ selector ( compare : ) ] ;
for ( NSNumber * reactIndex in sortedIndices ) {
2016-06-10 18:41:50 +00:00
NSInteger insertAtIndex = reactIndex . integerValue ;
// When performing a delete animation , views are not removed immediately
// from their container so we need to offset the insertion index if a view
// that will be removed appears earlier than the view we are inserting .
if ( isUIViewRegistry && _viewsToBeDeleted . count > 0 ) {
for ( NSInteger index = 0 ; index < insertAtIndex ; index + + ) {
UIView * subview = ( ( UIView * ) container ) . reactSubviews [ index ] ;
if ( [ _viewsToBeDeleted containsObject : subview ] ) {
insertAtIndex + + ;
}
}
}
2015-12-04 00:04:34 +00:00
[ container insertReactSubview : destinationsToChildrenToAdd [ reactIndex ]
2016-06-10 18:41:50 +00:00
atIndex : insertAtIndex ] ;
2015-02-20 04:10:52 +00:00
}
}
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( createView : ( nonnull NSNumber * ) reactTag
2015-04-08 15:52:48 +00:00
viewName : ( NSString * ) viewName
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
rootTag : ( __unused NSNumber * ) rootTag
2015-04-08 15:52:48 +00:00
props : ( NSDictionary * ) props )
2015-02-20 04:10:52 +00:00
{
2015-08-06 22:44:15 +00:00
RCTComponentData * componentData = _componentDataByName [ viewName ] ;
if ( componentData = = nil ) {
RCTLogError ( @ "No component found for view with name \" % @ \ "" , viewName ) ;
2015-02-20 04:10:52 +00:00
}
2015-08-06 22:44:15 +00:00
// Register shadow view
RCTShadowView * shadowView = [ componentData createShadowViewWithTag : reactTag ] ;
2015-11-16 17:16:25 +00:00
if ( shadowView ) {
[ componentData setProps : props forShadowView : shadowView ] ;
_shadowViewRegistry [ reactTag ] = shadowView ;
}
2015-04-07 21:26:43 +00:00
2015-07-17 10:53:15 +00:00
// Shadow view is the source of truth for background color this is a little
// bit counter - intuitive if people try to set background color when setting up
// the view , but it ' s the only way that makes sense given our threading model
UIColor * backgroundColor = shadowView . backgroundColor ;
2015-04-07 21:26:43 +00:00
2016-04-11 12:05:25 +00:00
// Dispatch view creation directly to the main thread instead of adding to
// UIBlocks array . This way , it doesn ' t get deferred until after layout .
__weak RCTUIManager * weakManager = self ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
RCTUIManager * uiManager = weakManager ;
if ( ! uiManager ) {
return ;
}
2015-12-30 22:15:38 +00:00
UIView * view = [ componentData createViewWithTag : reactTag ] ;
2015-11-16 17:16:25 +00:00
if ( view ) {
2016-03-17 16:39:42 +00:00
[ componentData setProps : props forView : view ] ; // Must be done before bgColor to prevent wrong default
2015-11-16 17:16:25 +00:00
if ( [ view respondsToSelector : @ selector ( setBackgroundColor : ) ] ) {
( ( UIView * ) view ) . backgroundColor = backgroundColor ;
}
if ( [ view respondsToSelector : @ selector ( reactBridgeDidFinishTransaction ) ] ) {
[ uiManager -> _bridgeTransactionListeners addObject : view ] ;
}
2016-04-11 12:05:25 +00:00
uiManager -> _viewRegistry [ reactTag ] = view ;
2016-03-16 17:17:09 +00:00
# if RCT_DEV
[ view _DEBUG _setReactShadowView : shadowView ] ;
# endif
2015-07-17 10:53:15 +00:00
}
2016-04-11 12:05:25 +00:00
} ) ;
2015-02-20 04:10:52 +00:00
}
2015-04-11 22:08:00 +00:00
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( updateView : ( nonnull NSNumber * ) reactTag
2015-08-11 22:12:55 +00:00
viewName : ( NSString * ) viewName // not always reliable , use shadowView . viewName if available
2015-04-08 15:52:48 +00:00
props : ( NSDictionary * ) props )
2015-02-20 04:10:52 +00:00
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
2015-08-11 22:12:55 +00:00
RCTComponentData * componentData = _componentDataByName [ shadowView . viewName ? : viewName ] ;
2015-08-06 22:44:15 +00:00
[ componentData setProps : props forShadowView : shadowView ] ;
2015-03-01 23:33:55 +00:00
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-07-17 10:53:15 +00:00
UIView * view = viewRegistry [ reactTag ] ;
2015-08-06 22:44:15 +00:00
[ componentData setProps : props forView : view ] ;
2015-07-17 10:53:15 +00:00
} ] ;
2015-02-20 04:10:52 +00:00
}
2017-01-27 02:14:40 +00:00
- ( void ) synchronouslyUpdateViewOnUIThread : ( NSNumber * ) reactTag
viewName : ( NSString * ) viewName
props : ( NSDictionary * ) props
{
RCTAssertMainQueue ( ) ;
RCTComponentData * componentData = _componentDataByName [ viewName ] ;
UIView * view = _viewRegistry [ reactTag ] ;
[ componentData setProps : props forView : view ] ;
}
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( focus : ( nonnull NSNumber * ) reactTag )
2015-02-20 04:10:52 +00:00
{
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-02-20 04:10:52 +00:00
UIView * newResponder = viewRegistry [ reactTag ] ;
2017-04-03 22:11:02 +00:00
[ newResponder reactFocus ] ;
2015-02-20 04:10:52 +00:00
} ] ;
}
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( blur : ( nonnull NSNumber * ) reactTag )
2015-02-20 04:10:52 +00:00
{
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-02-20 04:10:52 +00:00
UIView * currentResponder = viewRegistry [ reactTag ] ;
2017-04-03 22:11:02 +00:00
[ currentResponder reactBlur ] ;
2015-02-20 04:10:52 +00:00
} ] ;
}
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( findSubviewIn : ( nonnull NSNumber * ) reactTag atPoint : ( CGPoint ) point callback : ( RCTResponseSenderBlock ) callback )
{
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-05-26 18:16:25 +00:00
UIView * view = viewRegistry [ reactTag ] ;
UIView * target = [ view hitTest : point withEvent : nil ] ;
CGRect frame = [ target convertRect : target . bounds toView : view ] ;
while ( target . reactTag = = nil && target . superview ! = nil ) {
2015-08-24 10:14:33 +00:00
target = target . superview ;
2015-05-26 18:16:25 +00:00
}
callback ( @ [
2015-06-12 18:05:01 +00:00
RCTNullIfNil ( target . reactTag ) ,
2015-05-26 18:16:25 +00:00
@ ( frame . origin . x ) ,
@ ( frame . origin . y ) ,
@ ( frame . size . width ) ,
@ ( frame . size . height ) ,
] ) ;
} ] ;
}
2016-01-06 13:57:25 +00:00
RCT_EXPORT _METHOD ( dispatchViewManagerCommand : ( nonnull NSNumber * ) reactTag
commandID : ( NSInteger ) commandID
commandArgs : ( NSArray < id > * ) commandArgs )
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
RCTComponentData * componentData = _componentDataByName [ shadowView . viewName ] ;
Class managerClass = componentData . managerClass ;
RCTModuleData * moduleData = [ _bridge moduleDataForName : RCTBridgeModuleNameForClass ( managerClass ) ] ;
id < RCTBridgeMethod > method = moduleData . methods [ commandID ] ;
NSArray * args = [ @ [ reactTag ] arrayByAddingObjectsFromArray : commandArgs ] ;
[ method invokeWithBridge : _bridge module : componentData . manager arguments : args ] ;
}
2015-12-02 13:12:17 +00:00
- ( void ) partialBatchDidFlush
{
if ( self . unsafeFlushUIChangesBeforeBatchEnds ) {
[ self flushUIBlocks ] ;
}
}
2015-02-20 04:10:52 +00:00
- ( void ) batchDidComplete
2015-12-11 14:54:56 +00:00
{
[ self _layoutAndMount ] ;
}
/ * *
* Sets up animations , computes layout , creates UI mounting blocks for computed layout ,
* runs these blocks and all other already existing blocks .
* /
- ( void ) _layoutAndMount
2015-02-20 04:10:52 +00:00
{
2015-05-29 17:27:14 +00:00
// Gather blocks to be executed now that all view hierarchy manipulations have
// been completed ( note that these may still take place before layout has finished )
2015-08-06 22:44:15 +00:00
for ( RCTComponentData * componentData in _componentDataByName . allValues ) {
2016-02-26 16:17:39 +00:00
RCTViewManagerUIBlock uiBlock = [ componentData uiBlockToAmendWithShadowViewRegistry : _shadowViewRegistry ] ;
2015-05-29 17:27:14 +00:00
[ self addUIBlock : uiBlock ] ;
}
2017-05-08 19:40:07 +00:00
[ _observerCoordinator uiManagerWillPerformLayout : self ] ;
2015-02-20 04:10:52 +00:00
// Perform layout
for ( NSNumber * reactTag in _rootViewTags ) {
2016-03-21 10:20:49 +00:00
RCTRootShadowView * rootView = ( RCTRootShadowView * ) _shadowViewRegistry [ reactTag ] ;
2015-02-20 04:10:52 +00:00
[ self addUIBlock : [ self uiBlockWithLayoutUpdateForRootView : rootView ] ] ;
2017-05-08 19:40:07 +00:00
}
[ _observerCoordinator uiManagerDidPerformLayout : self ] ;
// Properies propagation
for ( NSNumber * reactTag in _rootViewTags ) {
RCTRootShadowView * rootView = ( RCTRootShadowView * ) _shadowViewRegistry [ reactTag ] ;
2016-03-21 10:20:49 +00:00
[ self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView : rootView ] ;
2015-02-20 04:10:52 +00:00
}
2015-03-01 23:33:55 +00:00
2015-11-17 18:19:48 +00:00
[ self addUIBlock : ^ ( RCTUIManager * uiManager , __unused NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
/ * *
* TODO ( tadeu ) : Remove it once and for all
* /
for ( id < RCTComponent > node in uiManager -> _bridgeTransactionListeners ) {
[ node reactBridgeDidFinishTransaction ] ;
}
} ] ;
2017-05-08 19:40:07 +00:00
[ _observerCoordinator uiManagerWillFlushUIBlocks : self ] ;
2017-03-28 12:30:00 +00:00
2015-03-25 00:37:03 +00:00
[ self flushUIBlocks ] ;
}
- ( void ) flushUIBlocks
{
2016-05-16 15:01:35 +00:00
RCTAssertThread ( RCTGetUIManagerQueue ( ) ,
@ "flushUIBlocks can only be called from the shadow queue" ) ;
2015-11-03 11:54:23 +00:00
2015-03-01 23:33:55 +00:00
// First copy the previous blocks into a temporary variable , then reset the
// pending blocks to a new array . This guards against mutation while
// processing the pending blocks in another thread .
2016-07-14 10:19:55 +00:00
NSArray < RCTViewManagerUIBlock > * previousPendingUIBlocks = _pendingUIBlocks ;
2015-08-17 14:35:34 +00:00
_pendingUIBlocks = [ NSMutableArray new ] ;
2015-03-01 23:33:55 +00:00
2015-10-27 12:07:45 +00:00
if ( previousPendingUIBlocks . count ) {
// Execute the previously queued UI blocks
RCTProfileBeginFlowEvent ( ) ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
RCTProfileEndFlowEvent ( ) ;
2016-09-05 18:11:37 +00:00
RCT_PROFILE _BEGIN _EVENT ( RCTProfileTagAlways , @ "-[UIManager flushUIBlocks]" , ( @ {
2017-05-13 00:57:12 +00:00
@ "count" : [ @ ( previousPendingUIBlocks . count ) stringValue ] ,
2016-09-05 18:11:37 +00:00
} ) ) ;
2015-10-27 12:07:45 +00:00
@ try {
2016-07-14 10:19:55 +00:00
for ( RCTViewManagerUIBlock block in previousPendingUIBlocks ) {
block ( self , self -> _viewRegistry ) ;
2015-10-27 12:07:45 +00:00
}
2015-10-27 12:07:40 +00:00
}
2015-10-27 12:07:45 +00:00
@ catch ( NSException * exception ) {
RCTLogError ( @ "Exception thrown while executing UI block: %@" , exception ) ;
}
2017-05-13 00:57:12 +00:00
RCT_PROFILE _END _EVENT ( RCTProfileTagAlways , @ "" ) ;
2015-04-20 11:55:05 +00:00
} ) ;
2015-10-27 12:07:45 +00:00
}
2015-02-20 04:10:52 +00:00
}
2015-12-11 14:54:56 +00:00
- ( void ) setNeedsLayout
{
// If there is an active batch layout will happen when batch finished , so we will wait for that .
// Otherwise we immidiately trigger layout .
2017-04-21 13:53:28 +00:00
if ( ! [ _bridge isBatchActive ] && ! [ _bridge isLoading ] ) {
2015-12-11 14:54:56 +00:00
[ self _layoutAndMount ] ;
}
}
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( measure : ( nonnull NSNumber * ) reactTag
2015-04-08 15:52:48 +00:00
callback : ( RCTResponseSenderBlock ) callback )
2015-02-20 04:10:52 +00:00
{
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-02-20 04:10:52 +00:00
UIView * view = viewRegistry [ reactTag ] ;
if ( ! view ) {
2015-07-14 19:03:17 +00:00
// this view was probably collapsed out
RCTLogWarn ( @ "measure cannot find view with tag #%@" , reactTag ) ;
callback ( @ [ ] ) ;
2015-02-20 04:10:52 +00:00
return ;
}
2016-02-25 17:38:23 +00:00
// If in a < Modal > , rootView will be the root of the modal container .
2015-02-20 04:10:52 +00:00
UIView * rootView = view ;
2016-02-25 17:38:23 +00:00
while ( rootView . superview && ! [ rootView isReactRootView ] ) {
2015-02-20 04:10:52 +00:00
rootView = rootView . superview ;
}
// By convention , all coordinates , whether they be touch coordinates , or
// measurement coordinates are with respect to the root view .
2016-02-25 17:38:23 +00:00
CGRect frame = view . frame ;
2017-01-11 21:42:22 +00:00
CGRect globalBounds = [ view convertRect : view . bounds toView : rootView ] ;
2015-02-20 04:10:52 +00:00
callback ( @ [
@ ( frame . origin . x ) ,
@ ( frame . origin . y ) ,
2017-01-11 21:42:22 +00:00
@ ( globalBounds . size . width ) ,
@ ( globalBounds . size . height ) ,
@ ( globalBounds . origin . x ) ,
@ ( globalBounds . origin . y ) ,
2015-02-20 04:10:52 +00:00
] ) ;
} ] ;
}
2016-03-01 14:50:33 +00:00
RCT_EXPORT _METHOD ( measureInWindow : ( nonnull NSNumber * ) reactTag
callback : ( RCTResponseSenderBlock ) callback )
{
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
UIView * view = viewRegistry [ reactTag ] ;
if ( ! view ) {
// this view was probably collapsed out
RCTLogWarn ( @ "measure cannot find view with tag #%@" , reactTag ) ;
callback ( @ [ ] ) ;
return ;
}
// Return frame coordinates in window
CGRect windowFrame = [ view . window convertRect : view . frame fromView : view . superview ] ;
callback ( @ [
@ ( windowFrame . origin . x ) ,
@ ( windowFrame . origin . y ) ,
@ ( windowFrame . size . width ) ,
@ ( windowFrame . size . height ) ,
] ) ;
} ] ;
}
2016-08-03 11:01:45 +00:00
/ * *
* Returs if the shadow view provided has the ` ancestor` shadow view as
* an actual ancestor .
* /
RCT_EXPORT _METHOD ( viewIsDescendantOf : ( nonnull NSNumber * ) reactTag
ancestor : ( nonnull NSNumber * ) ancestorReactTag
callback : ( RCTResponseSenderBlock ) callback )
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
RCTShadowView * ancestorShadowView = _shadowViewRegistry [ ancestorReactTag ] ;
if ( ! shadowView ) {
return ;
}
if ( ! ancestorShadowView ) {
return ;
}
BOOL viewIsAncestor = [ shadowView viewIsDescendantOf : ancestorShadowView ] ;
callback ( @ [ @ ( viewIsAncestor ) ] ) ;
}
2015-03-25 00:37:03 +00:00
static void RCTMeasureLayout ( RCTShadowView * view ,
RCTShadowView * ancestor ,
RCTResponseSenderBlock callback )
2015-02-20 04:10:52 +00:00
{
if ( ! view ) {
return ;
}
if ( ! ancestor ) {
return ;
}
2015-03-25 00:37:03 +00:00
CGRect result = [ view measureLayoutRelativeToAncestor : ancestor ] ;
2015-02-20 04:10:52 +00:00
if ( CGRectIsNull ( result ) ) {
2016-04-13 15:43:25 +00:00
RCTLogError ( @ "view %@ (tag #%@) is not a descendant of %@ (tag #%@)" ,
2015-03-01 23:33:55 +00:00
view , view . reactTag , ancestor , ancestor . reactTag ) ;
2015-02-20 04:10:52 +00:00
return ;
}
CGFloat leftOffset = result . origin . x ;
CGFloat topOffset = result . origin . y ;
CGFloat width = result . size . width ;
CGFloat height = result . size . height ;
if ( isnan ( leftOffset ) || isnan ( topOffset ) || isnan ( width ) || isnan ( height ) ) {
2015-03-01 23:33:55 +00:00
RCTLogError ( @ "Attempted to measure layout but offset or dimensions were NaN" ) ;
2015-02-20 04:10:52 +00:00
return ;
}
2015-03-25 00:37:03 +00:00
callback ( @ [ @ ( leftOffset ) , @ ( topOffset ) , @ ( width ) , @ ( height ) ] ) ;
2015-02-20 04:10:52 +00:00
}
/ * *
* Returns the computed recursive offset layout in a dictionary form . The
* returned values are relative to the ` ancestor` shadow view . Returns ` nil` , if
* the ` ancestor` shadow view is not actually an ` ancestor` . Does not touch
* anything on the main UI thread . Invokes supplied callback with ( x , y , width ,
* height ) .
* /
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( measureLayout : ( nonnull NSNumber * ) reactTag
relativeTo : ( nonnull NSNumber * ) ancestorReactTag
2015-06-15 14:53:45 +00:00
errorCallback : ( __unused RCTResponseSenderBlock ) errorCallback
2015-04-08 15:52:48 +00:00
callback : ( RCTResponseSenderBlock ) callback )
2015-02-20 04:10:52 +00:00
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
RCTShadowView * ancestorShadowView = _shadowViewRegistry [ ancestorReactTag ] ;
2015-03-25 00:37:03 +00:00
RCTMeasureLayout ( shadowView , ancestorShadowView , callback ) ;
2015-02-20 04:10:52 +00:00
}
/ * *
* Returns the computed recursive offset layout in a dictionary form . The
* returned values are relative to the ` ancestor` shadow view . Returns ` nil` , if
* the ` ancestor` shadow view is not actually an ` ancestor` . Does not touch
* anything on the main UI thread . Invokes supplied callback with ( x , y , width ,
* height ) .
* /
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( measureLayoutRelativeToParent : ( nonnull NSNumber * ) reactTag
2015-06-15 14:53:45 +00:00
errorCallback : ( __unused RCTResponseSenderBlock ) errorCallback
2015-04-08 15:52:48 +00:00
callback : ( RCTResponseSenderBlock ) callback )
2015-02-20 04:10:52 +00:00
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
2015-03-25 00:37:03 +00:00
RCTMeasureLayout ( shadowView , shadowView . reactSuperview , callback ) ;
2015-02-20 04:10:52 +00:00
}
/ * *
2015-04-27 20:55:01 +00:00
* Returns an array of computed offset layouts in a dictionary form . The layouts are of any React subviews
2015-02-20 04:10:52 +00:00
* that are immediate descendants to the parent view found within a specified rect . The dictionary result
* contains left , top , width , height and an index . The index specifies the position among the other subviews .
* Only layouts for views that are within the rect passed in are returned . Invokes the error callback if the
* passed in parent view does not exist . Invokes the supplied callback with the array of computed layouts .
* /
2015-04-27 10:58:30 +00:00
RCT_EXPORT _METHOD ( measureViewsInRect : ( CGRect ) rect
2015-07-31 13:55:47 +00:00
parentView : ( nonnull NSNumber * ) reactTag
2015-06-15 14:53:45 +00:00
errorCallback : ( __unused RCTResponseSenderBlock ) errorCallback
2015-04-08 15:52:48 +00:00
callback : ( RCTResponseSenderBlock ) callback )
2015-02-20 04:10:52 +00:00
{
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
if ( ! shadowView ) {
2015-03-01 23:33:55 +00:00
RCTLogError ( @ "Attempting to measure view that does not exist (tag #%@)" , reactTag ) ;
2015-02-20 04:10:52 +00:00
return ;
}
2015-11-03 22:45:46 +00:00
NSArray < RCTShadowView * > * childShadowViews = [ shadowView reactSubviews ] ;
NSMutableArray < NSDictionary * > * results =
[ [ NSMutableArray alloc ] initWithCapacity : childShadowViews . count ] ;
2015-04-19 19:55:46 +00:00
2015-06-15 14:53:45 +00:00
[ childShadowViews enumerateObjectsUsingBlock :
^ ( RCTShadowView * childShadowView , NSUInteger idx , __unused BOOL * stop ) {
2015-03-25 00:37:03 +00:00
CGRect childLayout = [ childShadowView measureLayoutRelativeToAncestor : shadowView ] ;
2015-02-20 04:10:52 +00:00
if ( CGRectIsNull ( childLayout ) ) {
2016-04-13 15:43:25 +00:00
RCTLogError ( @ "View %@ (tag #%@) is not a descendant of %@ (tag #%@)" ,
2015-03-01 23:33:55 +00:00
childShadowView , childShadowView . reactTag , shadowView , shadowView . reactTag ) ;
2015-02-20 04:10:52 +00:00
return ;
}
CGFloat leftOffset = childLayout . origin . x ;
CGFloat topOffset = childLayout . origin . y ;
CGFloat width = childLayout . size . width ;
CGFloat height = childLayout . size . height ;
2015-04-19 19:55:46 +00:00
if ( leftOffset <= rect . origin . x + rect . size . width &&
leftOffset + width >= rect . origin . x &&
topOffset <= rect . origin . y + rect . size . height &&
topOffset + height >= rect . origin . y ) {
2015-02-20 04:10:52 +00:00
// This view is within the layout rect
2015-03-17 10:51:58 +00:00
NSDictionary * result = @ { @ "index" : @ ( idx ) ,
2015-02-20 04:10:52 +00:00
@ "left" : @ ( leftOffset ) ,
@ "top" : @ ( topOffset ) ,
@ "width" : @ ( width ) ,
@ "height" : @ ( height ) } ;
[ results addObject : result ] ;
}
2015-03-17 10:51:58 +00:00
} ] ;
2015-02-20 04:10:52 +00:00
callback ( @ [ results ] ) ;
}
2016-02-23 10:26:11 +00:00
RCT_EXPORT _METHOD ( takeSnapshot : ( id / * NSString or NSNumber * / ) target
withOptions : ( NSDictionary * ) options
resolve : ( RCTPromiseResolveBlock ) resolve
reject : ( RCTPromiseRejectBlock ) reject )
{
2016-03-15 12:48:40 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2016-02-23 10:26:11 +00:00
// Get view
UIView * view ;
2016-02-24 17:05:28 +00:00
if ( target = = nil || [ target isEqual : @ "window" ] ) {
2016-02-23 10:26:11 +00:00
view = RCTKeyWindow ( ) ;
} else if ( [ target isKindOfClass : [ NSNumber class ] ] ) {
view = viewRegistry [ target ] ;
if ( ! view ) {
RCTLogError ( @ "No view found with reactTag: %@" , target ) ;
return ;
}
}
// Get options
CGSize size = [ RCTConvert CGSize : options ] ;
NSString * format = [ RCTConvert NSString : options [ @ "format" ] ? : @ "png" ] ;
// Capture image
if ( size . width < 0.1 || size . height < 0.1 ) {
size = view . bounds . size ;
}
UIGraphicsBeginImageContextWithOptions ( size , NO , 0 ) ;
BOOL success = [ view drawViewHierarchyInRect : ( CGRect ) { CGPointZero , size } afterScreenUpdates : YES ] ;
UIImage * image = UIGraphicsGetImageFromCurrentImageContext ( ) ;
UIGraphicsEndImageContext ( ) ;
if ( ! success || ! image ) {
2016-02-24 17:05:28 +00:00
reject ( RCTErrorUnspecified , @ "Failed to capture view snapshot." , nil ) ;
2016-02-23 10:26:11 +00:00
return ;
}
// Convert image to data ( on a background thread )
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
NSData * data ;
if ( [ format isEqualToString : @ "png" ] ) {
data = UIImagePNGRepresentation ( image ) ;
} else if ( [ format isEqualToString : @ "jpeg" ] ) {
CGFloat quality = [ RCTConvert CGFloat : options [ @ "quality" ] ? : @ 1 ] ;
data = UIImageJPEGRepresentation ( image , quality ) ;
} else {
RCTLogError ( @ "Unsupported image format: %@" , format ) ;
return ;
}
// Save to a temp file
NSError * error = nil ;
NSString * tempFilePath = RCTTempFilePath ( format , & error ) ;
if ( tempFilePath ) {
if ( [ data writeToFile : tempFilePath options : ( NSDataWritingOptions ) 0 error : & error ] ) {
resolve ( tempFilePath ) ;
return ;
}
}
// If we reached here , something went wrong
reject ( RCTErrorUnspecified , error . localizedDescription , error ) ;
} ) ;
} ] ;
}
2015-02-20 04:10:52 +00:00
/ * *
* JS sets what * it * considers to be the responder . Later , scroll views can use
* this in order to determine if scrolling is appropriate .
* /
2015-07-31 13:55:47 +00:00
RCT_EXPORT _METHOD ( setJSResponder : ( nonnull NSNumber * ) reactTag
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
blockNativeResponder : ( __unused BOOL ) blockNativeResponder )
2015-02-20 04:10:52 +00:00
{
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-07-17 10:53:15 +00:00
_jsResponder = viewRegistry [ reactTag ] ;
if ( ! _jsResponder ) {
RCTLogError ( @ "Invalid view set to be the JS responder - tag %zd" , reactTag ) ;
}
} ] ;
2015-02-20 04:10:52 +00:00
}
2015-04-08 15:52:48 +00:00
RCT_EXPORT _METHOD ( clearJSResponder )
2015-02-20 04:10:52 +00:00
{
2015-11-14 18:25:00 +00:00
[ self addUIBlock : ^ ( __unused RCTUIManager * uiManager , __unused NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
2015-02-20 04:10:52 +00:00
_jsResponder = nil ;
} ] ;
}
2015-11-14 18:25:00 +00:00
- ( NSDictionary < NSString * , id > * ) constantsToExport
2015-02-20 04:10:52 +00:00
{
2016-10-27 13:54:26 +00:00
NSMutableDictionary < NSString * , NSDictionary * > * constants = [ NSMutableDictionary new ] ;
2015-11-14 18:25:00 +00:00
NSMutableDictionary < NSString * , NSDictionary * > * directEvents = [ NSMutableDictionary new ] ;
NSMutableDictionary < NSString * , NSDictionary * > * bubblingEvents = [ NSMutableDictionary new ] ;
2015-02-20 04:10:52 +00:00
2016-10-27 13:54:26 +00:00
[ _componentDataByName enumerateKeysAndObjectsUsingBlock : ^ ( NSString * name , RCTComponentData * componentData , __unused BOOL * stop ) {
NSMutableDictionary < NSString * , id > * moduleConstants = [ NSMutableDictionary new ] ;
2015-02-20 04:10:52 +00:00
2015-11-18 12:45:22 +00:00
// Add manager class
2016-10-27 13:54:26 +00:00
moduleConstants [ @ "Manager" ] = RCTBridgeModuleNameForClass ( componentData . managerClass ) ;
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
// Add native props
2015-11-14 18:25:00 +00:00
NSDictionary < NSString * , id > * viewConfig = [ componentData viewConfig ] ;
2016-10-27 13:54:26 +00:00
moduleConstants [ @ "NativeProps" ] = viewConfig [ @ "propTypes" ] ;
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
// Add direct events
for ( NSString * eventName in viewConfig [ @ "directEvents" ] ) {
if ( ! directEvents [ eventName ] ) {
directEvents [ eventName ] = @ {
@ "registrationName" : [ eventName stringByReplacingCharactersInRange : ( NSRange ) { 0 , 3 } withString : @ "on" ] ,
} ;
}
if ( RCT_DEBUG && bubblingEvents [ eventName ] ) {
RCTLogError ( @ "Component '%@' re-registered bubbling event '%@' as a "
"direct event" , componentData . name , eventName ) ;
}
}
// Add bubbling events
for ( NSString * eventName in viewConfig [ @ "bubblingEvents" ] ) {
if ( ! bubblingEvents [ eventName ] ) {
NSString * bubbleName = [ eventName stringByReplacingCharactersInRange : ( NSRange ) { 0 , 3 } withString : @ "on" ] ;
bubblingEvents [ eventName ] = @ {
@ "phasedRegistrationNames" : @ {
@ "bubbled" : bubbleName ,
@ "captured" : [ bubbleName stringByAppendingString : @ "Capture" ] ,
}
} ;
}
if ( RCT_DEBUG && directEvents [ eventName ] ) {
RCTLogError ( @ "Component '%@' re-registered direct event '%@' as a "
"bubbling event" , componentData . name , eventName ) ;
}
}
2016-10-27 13:54:26 +00:00
RCTAssert ( ! constants [ name ] , @ "UIManager already has constants for %@" , componentData . name ) ;
constants [ name ] = moduleConstants ;
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
} ] ;
2015-02-20 04:10:52 +00:00
2016-10-27 13:54:26 +00:00
constants [ @ "customBubblingEventTypes" ] = bubblingEvents ;
constants [ @ "customDirectEventTypes" ] = directEvents ;
return constants ;
2015-02-20 04:10:52 +00:00
}
2015-04-08 15:52:48 +00:00
RCT_EXPORT _METHOD ( configureNextLayoutAnimation : ( NSDictionary * ) config
withCallback : ( RCTResponseSenderBlock ) callback
2015-06-15 14:53:45 +00:00
errorCallback : ( __unused RCTResponseSenderBlock ) errorCallback )
2015-02-20 04:10:52 +00:00
{
2016-04-25 07:08:42 +00:00
RCTLayoutAnimation * currentAnimation = _layoutAnimation ;
if ( currentAnimation && ! [ config isEqualToDictionary : currentAnimation . config ] ) {
RCTLogWarn ( @ "Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@." , currentAnimation . config , config ) ;
2015-02-20 04:10:52 +00:00
}
2016-04-25 07:08:42 +00:00
RCTLayoutAnimation * nextLayoutAnimation = [ [ RCTLayoutAnimation alloc ] initWithDictionary : config
2015-04-08 12:42:43 +00:00
callback : callback ] ;
2016-04-25 07:08:42 +00:00
// Set up next layout animation
[ self addUIBlock : ^ ( RCTUIManager * uiManager , __unused NSDictionary < NSNumber * , UIView * > * viewRegistry ) {
uiManager -> _layoutAnimation = nextLayoutAnimation ;
} ] ;
2015-02-20 04:10:52 +00:00
}
2016-08-29 05:46:42 +00:00
- ( void ) rootViewForReactTag : ( NSNumber * ) reactTag withCompletion : ( void ( ^ ) ( UIView * view ) ) completion
{
RCTAssertMainQueue ( ) ;
RCTAssert ( completion ! = nil , @ "Attempted to resolve rootView for tag %@ without a completion block" , reactTag ) ;
if ( reactTag = = nil ) {
completion ( nil ) ;
return ;
}
dispatch_async ( RCTGetUIManagerQueue ( ) , ^ {
NSNumber * rootTag = [ self _rootTagForReactTag : reactTag ] ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
UIView * rootView = nil ;
if ( rootTag ! = nil ) {
rootView = [ self viewForReactTag : rootTag ] ;
}
completion ( rootView ) ;
} ) ;
} ) ;
}
- ( NSNumber * ) _rootTagForReactTag : ( NSNumber * ) reactTag
{
RCTAssert ( ! RCTIsMainQueue ( ) , @ "Should be called on shadow queue" ) ;
if ( reactTag = = nil ) {
return nil ;
}
if ( RCTIsReactRootView ( reactTag ) ) {
return reactTag ;
}
NSNumber * rootTag = nil ;
RCTShadowView * shadowView = _shadowViewRegistry [ reactTag ] ;
while ( shadowView ) {
RCTShadowView * parent = [ shadowView reactSuperview ] ;
if ( ! parent && RCTIsReactRootView ( shadowView . reactTag ) ) {
rootTag = shadowView . reactTag ;
break ;
}
shadowView = parent ;
}
return rootTag ;
}
2015-02-20 04:10:52 +00:00
static UIView * _jsResponder ;
+ ( UIView * ) JSResponder
{
return _jsResponder ;
}
@ end
2015-03-01 23:33:55 +00:00
Deprecating/removing `setFrame`, `setLeftTop`, and co.
Summary:
Motivation:
* `RCTShadowView`'s `frame` property actually represents computed layout of the view. We must not use it as a setter for yoga node styles;
* Using `frame` and `setLeftTop` in existing way actually works only for view with absolute positioning, so it is super rare and special case;
* Internally, setting `frame` only make sense to `RootView`, and in that case there we always must not change `origin` we are introducing `setSize` method.
Changes:
* `[-RCTShadowView setFrame:]` was removed, `frame` property is readonly now;
* `[-RCTShadowView setLeftTop:]` was removed; no replacement provided;
* `[-RCTShadowView size]` read-write property was added;
* `[-RCTUIManager setFrame:forView:]` was deprecated, use (just introduced) `setSize:forView:` instead;
* `[-RCTUIManager setSize:forView:]` was added.
If you are still need some of removed methods, you are probably doing something wrong. Consider using `setIntrinsicContentSize`-family methods,
`setSize`-family methods, or (in the worst case) accessing `yogaNode` directly.
Reviewed By: javache
Differential Revision: D4491384
fbshipit-source-id: 56dd84567324c5a86e4c870a41c38322dc1224d2
2017-02-01 20:59:26 +00:00
@ implementation RCTUIManager ( Deprecated )
2017-03-23 18:25:28 +00:00
- ( void ) registerRootView : ( UIView * ) rootView withSizeFlexibility : ( __unused RCTRootViewSizeFlexibility ) sizeFlexibility
2017-02-20 07:05:42 +00:00
{
RCTLogWarn ( @ "Calling of `[-RCTUIManager registerRootView:withSizeFlexibility:]` which is deprecated." ) ;
[ self registerRootView : rootView ] ;
}
Deprecating/removing `setFrame`, `setLeftTop`, and co.
Summary:
Motivation:
* `RCTShadowView`'s `frame` property actually represents computed layout of the view. We must not use it as a setter for yoga node styles;
* Using `frame` and `setLeftTop` in existing way actually works only for view with absolute positioning, so it is super rare and special case;
* Internally, setting `frame` only make sense to `RootView`, and in that case there we always must not change `origin` we are introducing `setSize` method.
Changes:
* `[-RCTShadowView setFrame:]` was removed, `frame` property is readonly now;
* `[-RCTShadowView setLeftTop:]` was removed; no replacement provided;
* `[-RCTShadowView size]` read-write property was added;
* `[-RCTUIManager setFrame:forView:]` was deprecated, use (just introduced) `setSize:forView:` instead;
* `[-RCTUIManager setSize:forView:]` was added.
If you are still need some of removed methods, you are probably doing something wrong. Consider using `setIntrinsicContentSize`-family methods,
`setSize`-family methods, or (in the worst case) accessing `yogaNode` directly.
Reviewed By: javache
Differential Revision: D4491384
fbshipit-source-id: 56dd84567324c5a86e4c870a41c38322dc1224d2
2017-02-01 20:59:26 +00:00
- ( void ) setFrame : ( CGRect ) frame forView : ( UIView * ) view
{
RCTLogWarn ( @ "Calling of `[-RCTUIManager setFrame:forView:]` which is deprecated." ) ;
[ self setSize : frame . size forView : view ] ;
}
2017-03-08 13:56:27 +00:00
RCT_EXPORT _METHOD ( getContentSizeMultiplier : ( nonnull RCTResponseSenderBlock ) callback )
{
RCTLogWarn ( @ "`getContentSizeMultiplier` is deprecated. Instead, use `PixelRatio.getFontScale()` and listen to the `didUpdateDimensions` event." ) ;
callback ( @ [ @ ( _bridge . accessibilityManager . multiplier ) ] ) ;
}
Deprecating/removing `setFrame`, `setLeftTop`, and co.
Summary:
Motivation:
* `RCTShadowView`'s `frame` property actually represents computed layout of the view. We must not use it as a setter for yoga node styles;
* Using `frame` and `setLeftTop` in existing way actually works only for view with absolute positioning, so it is super rare and special case;
* Internally, setting `frame` only make sense to `RootView`, and in that case there we always must not change `origin` we are introducing `setSize` method.
Changes:
* `[-RCTShadowView setFrame:]` was removed, `frame` property is readonly now;
* `[-RCTShadowView setLeftTop:]` was removed; no replacement provided;
* `[-RCTShadowView size]` read-write property was added;
* `[-RCTUIManager setFrame:forView:]` was deprecated, use (just introduced) `setSize:forView:` instead;
* `[-RCTUIManager setSize:forView:]` was added.
If you are still need some of removed methods, you are probably doing something wrong. Consider using `setIntrinsicContentSize`-family methods,
`setSize`-family methods, or (in the worst case) accessing `yogaNode` directly.
Reviewed By: javache
Differential Revision: D4491384
fbshipit-source-id: 56dd84567324c5a86e4c870a41c38322dc1224d2
2017-02-01 20:59:26 +00:00
@ end
2015-03-01 23:33:55 +00:00
@ implementation RCTBridge ( RCTUIManager )
- ( RCTUIManager * ) uiManager
{
2015-11-25 11:09:00 +00:00
return [ self moduleForClass : [ RCTUIManager class ] ] ;
2015-03-01 23:33:55 +00:00
}
@ end