mirror of
https://github.com/status-im/react-native.git
synced 2025-01-12 18:44:25 +00:00
Refactor RCTUIManager
Summary: Moved the view creation & property binding logic out of RCTUIManager into a separate RCTComponentData class - this follows the pattern used with the bridge. I've also updated the property binding to use pre-allocated blocks for setting the values, which is more efficient than the previous system that re-contructed the selectors each time it was called. This should improve view update performance significantly.
This commit is contained in:
parent
aefdf82cdc
commit
deba13f698
@ -25,10 +25,10 @@
|
||||
|
||||
- (void)testDictionary
|
||||
{
|
||||
NSObject<RCTViewNodeProtocol> *myView = [[UIView alloc] init];
|
||||
id<RCTComponent> myView = [[UIView alloc] init];
|
||||
myView.reactTag = @4;
|
||||
|
||||
NSObject<RCTViewNodeProtocol> *myOtherView = [[UIView alloc] init];
|
||||
id<RCTComponent> myOtherView = [[UIView alloc] init];
|
||||
myOtherView.reactTag = @5;
|
||||
|
||||
RCTSparseArray *registry = [[RCTSparseArray alloc] init];
|
||||
|
@ -144,7 +144,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
||||
+ (ARTBrush *)ARTBrush:(id)json
|
||||
{
|
||||
NSArray *arr = [self NSArray:json];
|
||||
NSUInteger type = [self NSUInteger:arr[0]];
|
||||
NSUInteger type = [self NSUInteger:arr.firstObject];
|
||||
switch (type) {
|
||||
case 0: // solid color
|
||||
// These are probably expensive allocations since it's often the same value.
|
||||
|
@ -54,13 +54,6 @@ RCT_EXTERN NSString *const RCTDidCreateNativeModules;
|
||||
*/
|
||||
typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
|
||||
|
||||
/**
|
||||
* Register the given class as a bridge module. All modules must be registered
|
||||
* prior to the first bridge initialization.
|
||||
*
|
||||
*/
|
||||
RCT_EXTERN void RCTRegisterModule(Class);
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
@ -71,7 +64,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||
*/
|
||||
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new bridge with a custom RCTBridgeDelegate.
|
||||
*
|
||||
|
@ -48,6 +48,11 @@ NSArray *RCTGetModuleClasses(void)
|
||||
return RCTModuleClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given class as a bridge module. All modules must be registered
|
||||
* prior to the first bridge initialization.
|
||||
*/
|
||||
void RCTRegisterModule(Class);
|
||||
void RCTRegisterModule(Class moduleClass)
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
@ -57,7 +62,7 @@ void RCTRegisterModule(Class moduleClass)
|
||||
|
||||
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
||||
@"%@ does not conform to the RCTBridgeModule protocol",
|
||||
NSStringFromClass(moduleClass));
|
||||
moduleClass);
|
||||
|
||||
// Register module
|
||||
[RCTModuleClasses addObject:moduleClass];
|
||||
|
@ -94,7 +94,7 @@ extern dispatch_queue_t RCTJSThread;
|
||||
#define RCT_EXPORT_MODULE(js_name) \
|
||||
RCT_EXTERN void RCTRegisterModule(Class); \
|
||||
+ (NSString *)moduleName { return @#js_name; } \
|
||||
+ (void)load { RCTRegisterModule([self class]); }
|
||||
+ (void)load { RCTRegisterModule(self); }
|
||||
|
||||
/**
|
||||
* Wrap the parameter line of your method implementation with this macro to
|
||||
|
@ -136,21 +136,6 @@ typedef BOOL css_clip_t, css_backface_visibility_t;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This function will attempt to set a property using a json value by first
|
||||
* inferring the correct type from all available information, and then
|
||||
* applying an appropriate conversion method. If the property does not
|
||||
* exist, or the type cannot be inferred, the function will return NO.
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json);
|
||||
|
||||
/**
|
||||
* This function attempts to copy a property from the source object to the
|
||||
* destination object using KVC. If the property does not exist, or cannot
|
||||
* be set, it will do nothing and return NO.
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
|
||||
|
||||
/**
|
||||
* Underlying implementations of RCT_XXX_CONVERTER macros. Ignore these.
|
||||
*/
|
||||
|
@ -1056,177 +1056,3 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
|
||||
}), RCTAnimationTypeEaseInEaseOut, integerValue)
|
||||
|
||||
@end
|
||||
|
||||
BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keyPath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Get property setter
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
// Fail early
|
||||
if (![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@try {
|
||||
|
||||
NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type];
|
||||
switch (signature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_SET_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \
|
||||
set(target, setter, convert([RCTConvert class], type, json)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_SET_CASE(':', SEL)
|
||||
RCT_SET_CASE('*', const char *)
|
||||
RCT_SET_CASE('c', char)
|
||||
RCT_SET_CASE('C', unsigned char)
|
||||
RCT_SET_CASE('s', short)
|
||||
RCT_SET_CASE('S', unsigned short)
|
||||
RCT_SET_CASE('i', int)
|
||||
RCT_SET_CASE('I', unsigned int)
|
||||
RCT_SET_CASE('l', long)
|
||||
RCT_SET_CASE('L', unsigned long)
|
||||
RCT_SET_CASE('q', long long)
|
||||
RCT_SET_CASE('Q', unsigned long long)
|
||||
RCT_SET_CASE('f', float)
|
||||
RCT_SET_CASE('d', double)
|
||||
RCT_SET_CASE('B', BOOL)
|
||||
RCT_SET_CASE('^', void *)
|
||||
|
||||
case '@': {
|
||||
id (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend;
|
||||
void (*set)(id, SEL, id) = (typeof(set))objc_msgSend;
|
||||
set(target, setter, convert([RCTConvert class], type, json));
|
||||
break;
|
||||
}
|
||||
case '{':
|
||||
default: {
|
||||
|
||||
// Get converted value
|
||||
void *value = malloc(signature.methodReturnLength);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setTarget:[RCTConvert class]];
|
||||
[invocation setSelector:type];
|
||||
[invocation setArgument:&json atIndex:2];
|
||||
[invocation invoke];
|
||||
[invocation getReturnValue:value];
|
||||
|
||||
// Set converted value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
[invocation setArgument:value atIndex:2];
|
||||
[invocation invokeWithTarget:target];
|
||||
free(value);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"Exception thrown while attempting to set property '%@' of \
|
||||
'%@' with value '%@': %@", key, [target class], json, exception);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keyPath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
source = [source valueForKey:parts[i]];
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!source || !target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Get property getter
|
||||
SEL getter = NSSelectorFromString(key);
|
||||
|
||||
// Get property setter
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
// Fail early
|
||||
if (![source respondsToSelector:getter] || ![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMethodSignature *signature = [source methodSignatureForSelector:getter];
|
||||
switch (signature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_COPY_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*get)(id, SEL) = (typeof(get))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \
|
||||
set(target, setter, get(source, getter)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_COPY_CASE(':', SEL)
|
||||
RCT_COPY_CASE('*', const char *)
|
||||
RCT_COPY_CASE('c', char)
|
||||
RCT_COPY_CASE('C', unsigned char)
|
||||
RCT_COPY_CASE('s', short)
|
||||
RCT_COPY_CASE('S', unsigned short)
|
||||
RCT_COPY_CASE('i', int)
|
||||
RCT_COPY_CASE('I', unsigned int)
|
||||
RCT_COPY_CASE('l', long)
|
||||
RCT_COPY_CASE('L', unsigned long)
|
||||
RCT_COPY_CASE('q', long long)
|
||||
RCT_COPY_CASE('Q', unsigned long long)
|
||||
RCT_COPY_CASE('f', float)
|
||||
RCT_COPY_CASE('d', double)
|
||||
RCT_COPY_CASE('B', BOOL)
|
||||
RCT_COPY_CASE('^', void *)
|
||||
|
||||
case '@': {
|
||||
id (*get)(id, SEL) = (typeof(get))objc_msgSend;
|
||||
void (*set)(id, SEL, id) = (typeof(set))objc_msgSend;
|
||||
set(target, setter, get(source, getter));
|
||||
break;
|
||||
}
|
||||
case '{':
|
||||
default: {
|
||||
|
||||
// Get value
|
||||
void *value = malloc(signature.methodReturnLength);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&getter atIndex:1];
|
||||
[invocation invokeWithTarget:source];
|
||||
[invocation getReturnValue:value];
|
||||
|
||||
// Set value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
[invocation setArgument:value atIndex:2];
|
||||
[invocation invokeWithTarget:target];
|
||||
free(value);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id);
|
||||
typedef void (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id);
|
||||
|
||||
@implementation RCTMethodArgument
|
||||
|
||||
@ -47,7 +47,7 @@ typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id);
|
||||
{
|
||||
Class _moduleClass;
|
||||
SEL _selector;
|
||||
NSMethodSignature *_methodSignature;
|
||||
NSInvocation *_invocation;
|
||||
NSArray *_argumentBlocks;
|
||||
}
|
||||
|
||||
@ -135,16 +135,20 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||
methodName;
|
||||
});
|
||||
|
||||
// Get method signature
|
||||
_methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||
RCTAssert(_methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName);
|
||||
// Create method invocation
|
||||
NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||
RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[invocation setSelector:_selector];
|
||||
[invocation retainArguments];
|
||||
_invocation = invocation;
|
||||
|
||||
// Process arguments
|
||||
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
|
||||
NSUInteger numberOfArguments = methodSignature.numberOfArguments;
|
||||
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
||||
|
||||
#define RCT_ARG_BLOCK(_logic) \
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
|
||||
_logic \
|
||||
[invocation setArgument:&value atIndex:(index) + 2]; \
|
||||
}];
|
||||
@ -168,7 +172,7 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||
};
|
||||
|
||||
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
||||
const char *objcType = [_methodSignature getArgumentTypeAtIndex:i];
|
||||
const char *objcType = [methodSignature getArgumentTypeAtIndex:i];
|
||||
BOOL isNullableType = NO;
|
||||
RCTMethodArgument *argument = arguments[i - 2];
|
||||
NSString *typeName = argument.type;
|
||||
@ -176,45 +180,54 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||
if ([RCTConvert respondsToSelector:selector]) {
|
||||
switch (objcType[0]) {
|
||||
|
||||
#define RCT_CONVERT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
if (RCT_DEBUG && ([@#_type hasSuffix:@"*"] || [@#_type hasSuffix:@"Ref"] || [@#_type isEqualToString:@"id"])) { \
|
||||
isNullableType = YES; \
|
||||
} \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
#define RCT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_CONVERT_CASE(':', SEL)
|
||||
RCT_CONVERT_CASE('*', const char *)
|
||||
RCT_CONVERT_CASE('c', char)
|
||||
RCT_CONVERT_CASE('C', unsigned char)
|
||||
RCT_CONVERT_CASE('s', short)
|
||||
RCT_CONVERT_CASE('S', unsigned short)
|
||||
RCT_CONVERT_CASE('i', int)
|
||||
RCT_CONVERT_CASE('I', unsigned int)
|
||||
RCT_CONVERT_CASE('l', long)
|
||||
RCT_CONVERT_CASE('L', unsigned long)
|
||||
RCT_CONVERT_CASE('q', long long)
|
||||
RCT_CONVERT_CASE('Q', unsigned long long)
|
||||
RCT_CONVERT_CASE('f', float)
|
||||
RCT_CONVERT_CASE('d', double)
|
||||
RCT_CONVERT_CASE('B', BOOL)
|
||||
RCT_CONVERT_CASE('@', id)
|
||||
RCT_CONVERT_CASE('^', void *)
|
||||
RCT_CASE(_C_CHR, char)
|
||||
RCT_CASE(_C_UCHR, unsigned char)
|
||||
RCT_CASE(_C_SHT, short)
|
||||
RCT_CASE(_C_USHT, unsigned short)
|
||||
RCT_CASE(_C_INT, int)
|
||||
RCT_CASE(_C_UINT, unsigned int)
|
||||
RCT_CASE(_C_LNG, long)
|
||||
RCT_CASE(_C_ULNG, unsigned long)
|
||||
RCT_CASE(_C_LNG_LNG, long long)
|
||||
RCT_CASE(_C_ULNG_LNG, unsigned long long)
|
||||
RCT_CASE(_C_FLT, float)
|
||||
RCT_CASE(_C_DBL, double)
|
||||
RCT_CASE(_C_BOOL, BOOL)
|
||||
|
||||
case '{': {
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) {
|
||||
#define RCT_NULLABLE_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
isNullableType = YES; \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
|
||||
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||
void *returnValue = malloc(methodSignature.methodReturnLength);
|
||||
NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[_invocation setTarget:[RCTConvert class]];
|
||||
[_invocation setSelector:selector];
|
||||
[_invocation setArgument:&json atIndex:2];
|
||||
[_invocation invoke];
|
||||
[_invocation getReturnValue:returnValue];
|
||||
RCT_NULLABLE_CASE(_C_SEL, SEL)
|
||||
RCT_NULLABLE_CASE(_C_CHARPTR, const char *)
|
||||
RCT_NULLABLE_CASE(_C_PTR, void *)
|
||||
RCT_NULLABLE_CASE(_C_ID, id)
|
||||
|
||||
case _C_STRUCT_B: {
|
||||
|
||||
NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
||||
[typeInvocation setSelector:selector];
|
||||
[typeInvocation setTarget:[RCTConvert class]];
|
||||
|
||||
[argumentBlocks addObject:
|
||||
^(__unused RCTBridge *bridge, NSUInteger index, id json) {
|
||||
|
||||
void *returnValue = malloc(typeSignature.methodReturnLength);
|
||||
[typeInvocation setArgument:&json atIndex:2];
|
||||
[typeInvocation invoke];
|
||||
[typeInvocation getReturnValue:returnValue];
|
||||
|
||||
[invocation setArgument:returnValue atIndex:index + 2];
|
||||
|
||||
@ -323,13 +336,13 @@ case _value: { \
|
||||
|
||||
if (nullability == RCTNonnullable) {
|
||||
RCTArgumentBlock oldBlock = argumentBlocks[i - 2];
|
||||
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) {
|
||||
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSUInteger index, id json) {
|
||||
if (json == nil || json == (id)kCFNull) {
|
||||
RCTLogArgumentError(weakSelf, index, typeName, "must not be null");
|
||||
id null = nil;
|
||||
[invocation setArgument:&null atIndex:index + 2];
|
||||
} else {
|
||||
oldBlock(bridge, invocation, index, json);
|
||||
oldBlock(bridge, index, json);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -370,22 +383,17 @@ case _value: { \
|
||||
}
|
||||
}
|
||||
|
||||
// Create invocation (we can't re-use this as it wouldn't be thread-safe)
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature];
|
||||
[invocation setArgument:&_selector atIndex:1];
|
||||
[invocation retainArguments];
|
||||
|
||||
// Set arguments
|
||||
NSUInteger index = 0;
|
||||
for (id json in arguments) {
|
||||
id arg = RCTNilIfNull(json);
|
||||
RCTArgumentBlock block = _argumentBlocks[index];
|
||||
block(bridge, invocation, index, arg);
|
||||
block(bridge, index, arg);
|
||||
index++;
|
||||
}
|
||||
|
||||
// Invoke method
|
||||
[invocation invokeWithTarget:module];
|
||||
[_invocation invokeWithTarget:module];
|
||||
}
|
||||
|
||||
- (NSString *)methodName
|
||||
|
@ -244,7 +244,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex
|
||||
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:atIndex];
|
||||
RCTPerformanceLoggerEnd(RCTPLTTI);
|
||||
|
@ -18,7 +18,7 @@
|
||||
* Posted right before re-render happens. This is a chance for views to invalidate their state so
|
||||
* next render cycle will pick up updated views and layout appropriately.
|
||||
*/
|
||||
extern NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification;
|
||||
RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification;
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#import "RCTUIManager.h"
|
||||
|
||||
#import <objc/message.h>
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import "Layout.h"
|
||||
@ -18,6 +16,8 @@
|
||||
#import "RCTAnimationType.h"
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTComponent.h"
|
||||
#import "RCTComponentData.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
@ -30,15 +30,12 @@
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import "RCTViewNodeProtocol.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
typedef void (^react_view_node_block_t)(id<RCTViewNodeProtocol>);
|
||||
|
||||
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_block_t block)
|
||||
static void RCTTraverseViewNodes(id<RCTComponent> view, void (^block)(id<RCTComponent>))
|
||||
{
|
||||
if (view.reactTag) block(view);
|
||||
for (id<RCTViewNodeProtocol> subview in view.reactSubviews) {
|
||||
for (id<RCTComponent> subview in view.reactSubviews) {
|
||||
RCTTraverseViewNodes(subview, block);
|
||||
}
|
||||
}
|
||||
@ -199,10 +196,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
|
||||
RCTLayoutAnimation *_layoutAnimation; // Main thread only
|
||||
|
||||
// Keyed by viewName
|
||||
NSMutableDictionary *_defaultShadowViews; // RCT thread only
|
||||
NSMutableDictionary *_defaultViews; // Main thread only
|
||||
NSDictionary *_viewManagers;
|
||||
NSDictionary *_viewConfigs;
|
||||
NSDictionary *_componentDataByName;
|
||||
|
||||
NSMutableSet *_bridgeTransactionListeners;
|
||||
}
|
||||
@ -216,42 +210,6 @@ RCT_EXPORT_MODULE()
|
||||
*/
|
||||
extern NSString *RCTBridgeModuleNameForClass(Class cls);
|
||||
|
||||
/**
|
||||
* This function derives the view name automatically
|
||||
* from the module name.
|
||||
*/
|
||||
static NSString *RCTViewNameForModuleName(NSString *moduleName)
|
||||
{
|
||||
NSString *name = moduleName;
|
||||
RCTAssert(name.length, @"Invalid moduleName '%@'", moduleName);
|
||||
if ([name hasSuffix:@"Manager"]) {
|
||||
name = [name substringToIndex:name.length - @"Manager".length];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// TODO: only send name once instead of a dictionary of name and type keyed by name
|
||||
static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
Method *methods = class_copyMethodList(object_getClass(managerClass), &count);
|
||||
NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithCapacity:count];
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
Method method = methods[i];
|
||||
NSString *methodName = NSStringFromSelector(method_getName(method));
|
||||
if ([methodName hasPrefix:@"getPropConfig"]) {
|
||||
NSRange nameRange = [methodName rangeOfString:@"_"];
|
||||
if (nameRange.length) {
|
||||
NSString *name = [methodName substringFromIndex:nameRange.location + 1];
|
||||
NSString *type = [managerClass valueForKey:methodName];
|
||||
props[name] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(methods);
|
||||
return props;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
@ -260,10 +218,6 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
|
||||
_pendingUIBlocksLock = [[NSLock alloc] init];
|
||||
|
||||
_defaultShadowViews = [[NSMutableDictionary alloc] init];
|
||||
_defaultViews = [[NSMutableDictionary alloc] init];
|
||||
|
||||
_viewManagerRegistry = [[RCTSparseArray alloc] init];
|
||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||
_viewRegistry = [[RCTSparseArray alloc] init];
|
||||
|
||||
@ -335,19 +289,15 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||
|
||||
// Get view managers from bridge
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *viewConfigs = [[NSMutableDictionary alloc] init];
|
||||
[_bridge.modules enumerateKeysAndObjectsUsingBlock:
|
||||
^(NSString *moduleName, RCTViewManager *manager, __unused BOOL *stop) {
|
||||
NSMutableDictionary *componentDataByName = [[NSMutableDictionary alloc] init];
|
||||
for (RCTViewManager *manager in _bridge.modules.allValues) {
|
||||
if ([manager isKindOfClass:[RCTViewManager class]]) {
|
||||
NSString *viewName = RCTViewNameForModuleName(moduleName);
|
||||
viewManagers[viewName] = manager;
|
||||
viewConfigs[viewName] = RCTViewConfigForModule([manager class]);
|
||||
RCTComponentData *componentData = [[RCTComponentData alloc] initWithManager:manager];
|
||||
componentDataByName[componentData.name] = componentData;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
_viewManagers = [viewManagers copy];
|
||||
_viewConfigs = [viewConfigs copy];
|
||||
_componentDataByName = [componentDataByName copy];
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
@ -435,8 +385,8 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
*/
|
||||
- (void)_purgeChildren:(NSArray *)children fromRegistry:(RCTSparseArray *)registry
|
||||
{
|
||||
for (id<RCTViewNodeProtocol> child in children) {
|
||||
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
|
||||
for (id<RCTComponent> child in children) {
|
||||
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTComponent> subview) {
|
||||
RCTAssert(![subview isReactRootView], @"Root views should not be unregistered");
|
||||
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
|
||||
[(id<RCTInvalidating>)subview invalidate];
|
||||
@ -529,7 +479,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
// properties that aren't related to layout.
|
||||
NSMutableArray *updateBlocks = [[NSMutableArray alloc] init];
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
RCTViewManager *manager = _viewManagerRegistry[shadowView.reactTag];
|
||||
RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager];
|
||||
RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView];
|
||||
if (block) [updateBlocks addObject:block];
|
||||
}
|
||||
@ -601,7 +551,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
/**
|
||||
* TODO(tadeu): Remove it once and for all
|
||||
*/
|
||||
for (id<RCTViewNodeProtocol> node in _bridgeTransactionListeners) {
|
||||
for (id<RCTComponent> node in _bridgeTransactionListeners) {
|
||||
[node reactBridgeDidFinishTransaction];
|
||||
}
|
||||
};
|
||||
@ -625,7 +575,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
||||
*/
|
||||
RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containerID)
|
||||
{
|
||||
id<RCTViewNodeProtocol> container = _shadowViewRegistry[containerID];
|
||||
id<RCTComponent> container = _shadowViewRegistry[containerID];
|
||||
RCTAssert(container != nil, @"container view (for ID %@) not found", containerID);
|
||||
|
||||
NSUInteger subviewsCount = [container reactSubviews].count;
|
||||
@ -648,7 +598,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
|
||||
*
|
||||
* @returns Array of removed items.
|
||||
*/
|
||||
- (NSArray *)_childrenToRemoveFromContainer:(id<RCTViewNodeProtocol>)container
|
||||
- (NSArray *)_childrenToRemoveFromContainer:(id<RCTComponent>)container
|
||||
atIndices:(NSArray *)atIndices
|
||||
{
|
||||
// If there are no indices to move or the container has no subviews don't bother
|
||||
@ -672,7 +622,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
|
||||
return removedChildren;
|
||||
}
|
||||
|
||||
- (void)_removeChildren:(NSArray *)children fromContainer:(id<RCTViewNodeProtocol>)container
|
||||
- (void)_removeChildren:(NSArray *)children fromContainer:(id<RCTComponent>)container
|
||||
{
|
||||
for (id removedChild in children) {
|
||||
[container removeReactSubview:removedChild];
|
||||
@ -749,7 +699,7 @@ RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerReactTag
|
||||
removeAtIndices:(NSArray *)removeAtIndices
|
||||
registry:(RCTSparseArray *)registry
|
||||
{
|
||||
id<RCTViewNodeProtocol> container = registry[containerReactTag];
|
||||
id<RCTComponent> container = registry[containerReactTag];
|
||||
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");
|
||||
|
||||
@ -781,87 +731,19 @@ RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerReactTag
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL RCTCallPropertySetter(NSString *key, SEL setter, id value, id view, id defaultView, RCTViewManager *manager)
|
||||
{
|
||||
// TODO: cache respondsToSelector tests
|
||||
if ([manager respondsToSelector:setter]) {
|
||||
|
||||
if (value == (id)kCFNull) {
|
||||
value = nil;
|
||||
}
|
||||
|
||||
void (^block)() = ^{
|
||||
((void (*)(id, SEL, id, id, id))objc_msgSend)(manager, setter, value, view, defaultView);
|
||||
};
|
||||
|
||||
if (RCT_DEBUG) {
|
||||
NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([manager class]));
|
||||
NSString *logPrefix = [NSString stringWithFormat:
|
||||
@"Error setting property '%@' of %@ with tag #%@: ",
|
||||
key, viewName, [view reactTag]];
|
||||
|
||||
RCTPerformBlockWithLogPrefix(block, logPrefix);
|
||||
} else {
|
||||
block();
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
static void RCTSetViewProps(NSDictionary *props, UIView *view,
|
||||
UIView *defaultView, RCTViewManager *manager)
|
||||
{
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, __unused BOOL *stop) {
|
||||
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forView:withDefaultView:", key]);
|
||||
RCTCallPropertySetter(key, setter, obj, view, defaultView, manager);
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView,
|
||||
RCTShadowView *defaultView, RCTViewManager *manager)
|
||||
{
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, __unused BOOL *stop) {
|
||||
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forShadowView:withDefaultView:", key]);
|
||||
RCTCallPropertySetter(key, setter, obj, shadowView, defaultView, manager);
|
||||
|
||||
}];
|
||||
|
||||
// Update layout
|
||||
[shadowView updateLayout];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag
|
||||
viewName:(NSString *)viewName
|
||||
rootTag:(__unused NSNumber *)rootTag
|
||||
props:(NSDictionary *)props)
|
||||
{
|
||||
RCTViewManager *manager = _viewManagers[viewName];
|
||||
if (manager == nil) {
|
||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", viewName);
|
||||
manager = [[RCTViewManager alloc] init];
|
||||
RCTComponentData *componentData = _componentDataByName[viewName];
|
||||
if (componentData == nil) {
|
||||
RCTLogError(@"No component found for view with name \"%@\"", viewName);
|
||||
}
|
||||
|
||||
// Register manager
|
||||
_viewManagerRegistry[reactTag] = manager;
|
||||
|
||||
RCTShadowView *shadowView = [manager shadowView];
|
||||
if (shadowView) {
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!_defaultShadowViews[viewName]) {
|
||||
_defaultShadowViews[viewName] = [manager shadowView];
|
||||
}
|
||||
|
||||
// Set properties
|
||||
shadowView.viewName = viewName;
|
||||
shadowView.reactTag = reactTag;
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], manager);
|
||||
}
|
||||
// Register shadow view
|
||||
RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag];
|
||||
[componentData setProps:props forShadowView:shadowView];
|
||||
_shadowViewRegistry[reactTag] = shadowView;
|
||||
|
||||
// Shadow view is the source of truth for background color this is a little
|
||||
@ -870,51 +752,30 @@ RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag
|
||||
UIColor *backgroundColor = shadowView.backgroundColor;
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
RCTAssertMainThread();
|
||||
|
||||
UIView *view = [manager view];
|
||||
if (view) {
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!uiManager->_defaultViews[viewName]) {
|
||||
// Note the default is setup after the props are read for the first time
|
||||
// ever for this className - this is ok because we only use the default
|
||||
// for restoring defaults, which never happens on first creation.
|
||||
uiManager->_defaultViews[viewName] = [manager view];
|
||||
}
|
||||
|
||||
// Set properties
|
||||
view.reactTag = reactTag;
|
||||
view.backgroundColor = backgroundColor;
|
||||
if ([view isKindOfClass:[UIView class]]) {
|
||||
view.multipleTouchEnabled = YES;
|
||||
view.userInteractionEnabled = YES; // required for touch handling
|
||||
view.layer.allowsGroupOpacity = YES; // required for touch handling
|
||||
}
|
||||
RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], manager);
|
||||
|
||||
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
||||
[uiManager->_bridgeTransactionListeners addObject:view];
|
||||
}
|
||||
id<RCTComponent> view = [componentData createViewWithTag:reactTag];
|
||||
if ([view respondsToSelector:@selector(setBackgroundColor:)]) {
|
||||
[(UIView *)view setBackgroundColor:backgroundColor];
|
||||
}
|
||||
[componentData setProps:props forView:view];
|
||||
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
||||
[uiManager->_bridgeTransactionListeners addObject:view];
|
||||
}
|
||||
viewRegistry[reactTag] = view;
|
||||
}];
|
||||
}
|
||||
|
||||
// TODO: remove viewName param as it isn't needed
|
||||
RCT_EXPORT_METHOD(updateView:(nonnull NSNumber *)reactTag
|
||||
viewName:(__unused NSString *)_
|
||||
viewName:(NSString *)viewName
|
||||
props:(NSDictionary *)props)
|
||||
{
|
||||
RCTViewManager *viewManager = _viewManagerRegistry[reactTag];
|
||||
NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([viewManager class]));
|
||||
RCTComponentData *componentData = _componentDataByName[viewName];
|
||||
|
||||
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], viewManager);
|
||||
[componentData setProps:props forShadowView:shadowView];
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
[self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], viewManager);
|
||||
[componentData setProps:props forView:view];
|
||||
}];
|
||||
}
|
||||
|
||||
@ -962,8 +823,8 @@ RCT_EXPORT_METHOD(findSubviewIn:(nonnull NSNumber *)reactTag atPoint:(CGPoint)po
|
||||
RCTProfileBeginEvent();
|
||||
// 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)
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
for (RCTComponentData *componentData in _componentDataByName.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [componentData.manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
|
||||
@ -1356,7 +1217,8 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||
},
|
||||
} mutableCopy];
|
||||
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
for (RCTComponentData *componentData in _componentDataByName.allValues) {
|
||||
RCTViewManager *manager = componentData.manager;
|
||||
if (RCTClassOverridesInstanceMethod([manager class], @selector(customBubblingEventTypes))) {
|
||||
NSDictionary *eventTypes = [manager customBubblingEventTypes];
|
||||
for (NSString *eventName in eventTypes) {
|
||||
@ -1417,7 +1279,8 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||
},
|
||||
} mutableCopy];
|
||||
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
for (RCTComponentData *componentData in _componentDataByName.allValues) {
|
||||
RCTViewManager *manager = componentData.manager;
|
||||
if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) {
|
||||
NSDictionary *eventTypes = [manager customDirectEventTypes];
|
||||
if (RCT_DEV) {
|
||||
@ -1453,9 +1316,9 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||
},
|
||||
} mutableCopy];
|
||||
|
||||
[_viewManagers enumerateKeysAndObjectsUsingBlock:
|
||||
^(NSString *name, RCTViewManager *manager, __unused BOOL *stop) {
|
||||
|
||||
[_componentDataByName enumerateKeysAndObjectsUsingBlock:
|
||||
^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
|
||||
RCTViewManager *manager = componentData.manager;
|
||||
NSMutableDictionary *constantsNamespace =
|
||||
[NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]];
|
||||
|
||||
@ -1469,7 +1332,7 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||
}
|
||||
|
||||
// Add native props
|
||||
constantsNamespace[@"NativeProps"] = _viewConfigs[name];
|
||||
constantsNamespace[@"NativeProps"] = [componentData viewConfig];
|
||||
|
||||
allJSConstants[name] = [constantsNamespace copy];
|
||||
}];
|
||||
|
@ -27,6 +27,7 @@
|
||||
1385D0341B665AAE000A309B /* RCTModuleMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 1385D0331B665AAE000A309B /* RCTModuleMap.m */; };
|
||||
138D6A141B53CD290074A87E /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A131B53CD290074A87E /* RCTCache.m */; };
|
||||
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; };
|
||||
13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AB90C01B6FA36700713B4F /* RCTComponentData.m */; };
|
||||
13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AF20441AE707F9005F5298 /* RCTSlider.m */; };
|
||||
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; };
|
||||
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */; };
|
||||
@ -139,6 +140,8 @@
|
||||
138D6A131B53CD290074A87E /* RCTCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCache.m; sourceTree = "<group>"; };
|
||||
13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = "<group>"; };
|
||||
13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = "<group>"; };
|
||||
13AB90BF1B6FA36700713B4F /* RCTComponentData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTComponentData.h; sourceTree = "<group>"; };
|
||||
13AB90C01B6FA36700713B4F /* RCTComponentData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTComponentData.m; sourceTree = "<group>"; };
|
||||
13AF1F851AE6E777005F5298 /* RCTDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = "<group>"; };
|
||||
13AF20431AE707F8005F5298 /* RCTSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSlider.h; sourceTree = "<group>"; };
|
||||
13AF20441AE707F9005F5298 /* RCTSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSlider.m; sourceTree = "<group>"; };
|
||||
@ -172,7 +175,7 @@
|
||||
13C156041AB1A2840079392D /* RCTWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewManager.m; sourceTree = "<group>"; };
|
||||
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = "<group>"; };
|
||||
13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = "<group>"; };
|
||||
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = "<group>"; };
|
||||
13C325281AA63B6A0048765F /* RCTComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTComponent.h; sourceTree = "<group>"; };
|
||||
13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderDrawing.h; sourceTree = "<group>"; };
|
||||
13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBorderDrawing.m; sourceTree = "<group>"; };
|
||||
13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = "<group>"; };
|
||||
@ -325,6 +328,9 @@
|
||||
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */,
|
||||
13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */,
|
||||
13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */,
|
||||
13C325281AA63B6A0048765F /* RCTComponent.h */,
|
||||
13AB90BF1B6FA36700713B4F /* RCTComponentData.h */,
|
||||
13AB90C01B6FA36700713B4F /* RCTComponentData.m */,
|
||||
13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */,
|
||||
13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */,
|
||||
13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */,
|
||||
@ -389,7 +395,6 @@
|
||||
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */,
|
||||
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */,
|
||||
13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */,
|
||||
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */,
|
||||
13C156011AB1A2840079392D /* RCTWebView.h */,
|
||||
13C156021AB1A2840079392D /* RCTWebView.m */,
|
||||
13C156031AB1A2840079392D /* RCTWebViewManager.h */,
|
||||
@ -653,6 +658,7 @@
|
||||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */,
|
||||
83A1FE8F1B62643A00BE0E65 /* RCTModalHostViewManager.m in Sources */,
|
||||
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */,
|
||||
13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */,
|
||||
138D6A141B53CD290074A87E /* RCTCache.m in Sources */,
|
||||
13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */,
|
||||
);
|
||||
|
@ -7,19 +7,21 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
/**
|
||||
* Logical node in a tree of application components. Both `ShadowView`s and
|
||||
* `UIView+React`s conform to this. Allows us to write utilities that
|
||||
* reason about trees generally.
|
||||
* Logical node in a tree of application components. Both `ShadowView` and
|
||||
* `UIView` conforms to this. Allows us to write utilities that reason about
|
||||
* trees generally.
|
||||
*/
|
||||
@protocol RCTViewNodeProtocol <NSObject>
|
||||
@protocol RCTComponent <NSObject>
|
||||
|
||||
@property (nonatomic, copy) NSNumber *reactTag;
|
||||
|
||||
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex;
|
||||
- (void)removeReactSubview:(id<RCTViewNodeProtocol>)subview;
|
||||
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex;
|
||||
- (void)removeReactSubview:(id<RCTComponent>)subview;
|
||||
- (NSArray *)reactSubviews;
|
||||
- (id<RCTViewNodeProtocol>)reactSuperview;
|
||||
- (id<RCTComponent>)reactSuperview;
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point;
|
||||
|
||||
// View/ShadowView is a root view
|
32
React/Views/RCTComponentData.h
Normal file
32
React/Views/RCTComponentData.h
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTComponent.h"
|
||||
#import "RCTDefines.h"
|
||||
|
||||
@class RCTShadowView;
|
||||
@class RCTViewManager;
|
||||
|
||||
@interface RCTComponentData : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *name;
|
||||
@property (nonatomic, strong, readonly) RCTViewManager *manager;
|
||||
|
||||
- (instancetype)initWithManager:(RCTViewManager *)manager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (id<RCTComponent>)createViewWithTag:(NSNumber *)tag;
|
||||
- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag;
|
||||
- (void)setProps:(NSDictionary *)props forView:(id<RCTComponent>)view;
|
||||
- (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowView *)shadowView;
|
||||
|
||||
- (NSDictionary *)viewConfig;
|
||||
|
||||
@end
|
321
React/Views/RCTComponentData.m
Normal file
321
React/Views/RCTComponentData.m
Normal file
@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 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 "RCTComponentData.h"
|
||||
|
||||
#import <objc/message.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
typedef void (^RCTPropBlock)(id<RCTComponent> view, id json);
|
||||
|
||||
@interface RCTComponentProp : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *type;
|
||||
@property (nonatomic, copy) RCTPropBlock propBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTComponentProp
|
||||
|
||||
- (instancetype)initWithType:(NSString *)type
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_type = [type copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTComponentData
|
||||
{
|
||||
id<RCTComponent> _defaultView;
|
||||
RCTShadowView *_defaultShadowView;
|
||||
NSMutableDictionary *_viewPropBlocks;
|
||||
NSMutableDictionary *_shadowPropBlocks;
|
||||
}
|
||||
|
||||
- (instancetype)initWithManager:(RCTViewManager *)manager
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_manager = manager;
|
||||
_viewPropBlocks = [[NSMutableDictionary alloc] init];
|
||||
_shadowPropBlocks = [[NSMutableDictionary alloc] init];
|
||||
|
||||
_name = RCTBridgeModuleNameForClass([manager class]);
|
||||
RCTAssert(_name.length, @"Invalid moduleName '%@'", _name);
|
||||
if ([_name hasSuffix:@"Manager"]) {
|
||||
_name = [_name substringToIndex:_name.length - @"Manager".length];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-init)
|
||||
|
||||
- (id<RCTComponent>)createViewWithTag:(NSNumber *)tag
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
id<RCTComponent> view = (id<RCTComponent>)[_manager view];
|
||||
view.reactTag = tag;
|
||||
if ([view isKindOfClass:[UIView class]]) {
|
||||
((UIView *)view).multipleTouchEnabled = YES;
|
||||
((UIView *)view).userInteractionEnabled = YES; // required for touch handling
|
||||
((UIView *)view).layer.allowsGroupOpacity = YES; // required for touch handling
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag
|
||||
{
|
||||
RCTShadowView *shadowView = [_manager shadowView];
|
||||
shadowView.reactTag = tag;
|
||||
shadowView.viewName = _name;
|
||||
return shadowView;
|
||||
}
|
||||
|
||||
- (RCTPropBlock)propBlockForKey:(NSString *)name defaultView:(id)defaultView
|
||||
{
|
||||
BOOL shadowView = [defaultView isKindOfClass:[RCTShadowView class]];
|
||||
NSMutableDictionary *propBlocks = shadowView ? _shadowPropBlocks : _viewPropBlocks;
|
||||
RCTPropBlock propBlock = propBlocks[name];
|
||||
if (!propBlock) {
|
||||
|
||||
__weak RCTComponentData *weakSelf = self;
|
||||
|
||||
// Get type
|
||||
SEL type = NULL;
|
||||
NSString *keyPath = nil;
|
||||
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"propConfig%@_%@", shadowView ? @"Shadow" : @"", name]);
|
||||
Class managerClass = [_manager class];
|
||||
if ([managerClass respondsToSelector:selector]) {
|
||||
NSArray *typeAndKeyPath = ((NSArray *(*)(id, SEL))objc_msgSend)(managerClass, selector);
|
||||
type = NSSelectorFromString([typeAndKeyPath[0] stringByAppendingString:@":"]);
|
||||
keyPath = typeAndKeyPath.count > 1 ? typeAndKeyPath[1] : nil;
|
||||
} else {
|
||||
propBlock = ^(__unused id view, __unused id json) {};
|
||||
propBlocks[name] = propBlock;
|
||||
return propBlock;
|
||||
}
|
||||
|
||||
// Check for custom setter
|
||||
if ([keyPath isEqualToString:@"__custom__"]) {
|
||||
|
||||
// Get custom setter
|
||||
SEL customSetter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:for%@View:withDefaultView:", name, shadowView ? @"Shadow" : @""]);
|
||||
|
||||
propBlock = ^(id<RCTComponent> view, id json) {
|
||||
((void (*)(id, SEL, id, id, id))objc_msgSend)(
|
||||
weakSelf.manager, customSetter, json == (id)kCFNull ? nil : json, view, defaultView
|
||||
);
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
// Disect keypath
|
||||
NSString *key = name;
|
||||
NSArray *parts = [keyPath componentsSeparatedByString:@"."];
|
||||
if (parts) {
|
||||
key = [parts lastObject];
|
||||
parts = [parts subarrayWithRange:(NSRange){0, parts.count - 1}];
|
||||
}
|
||||
|
||||
// Get property getter
|
||||
SEL getter = NSSelectorFromString(key);
|
||||
|
||||
// Get property setter
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
// Build setter block
|
||||
void (^setterBlock)(id target, id source, id json) = nil;
|
||||
NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type];
|
||||
switch (typeSignature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
_type (*get)(id, SEL) = (typeof(get))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \
|
||||
setterBlock = ^(id target, id source, id json) { \
|
||||
set(target, setter, json ? convert([RCTConvert class], type, json) : get(source, getter)); \
|
||||
}; \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_CASE(_C_SEL, SEL)
|
||||
RCT_CASE(_C_CHARPTR, const char *)
|
||||
RCT_CASE(_C_CHR, char)
|
||||
RCT_CASE(_C_UCHR, unsigned char)
|
||||
RCT_CASE(_C_SHT, short)
|
||||
RCT_CASE(_C_USHT, unsigned short)
|
||||
RCT_CASE(_C_INT, int)
|
||||
RCT_CASE(_C_UINT, unsigned int)
|
||||
RCT_CASE(_C_LNG, long)
|
||||
RCT_CASE(_C_ULNG, unsigned long)
|
||||
RCT_CASE(_C_LNG_LNG, long long)
|
||||
RCT_CASE(_C_ULNG_LNG, unsigned long long)
|
||||
RCT_CASE(_C_FLT, float)
|
||||
RCT_CASE(_C_DBL, double)
|
||||
RCT_CASE(_C_BOOL, BOOL)
|
||||
RCT_CASE(_C_PTR, void *)
|
||||
RCT_CASE(_C_ID, id)
|
||||
|
||||
case _C_STRUCT_B:
|
||||
default: {
|
||||
|
||||
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
||||
[typeInvocation setSelector:type];
|
||||
[typeInvocation setTarget:[RCTConvert class]];
|
||||
|
||||
__block NSInvocation *sourceInvocation = nil;
|
||||
__block NSInvocation *targetInvocation = nil;
|
||||
|
||||
setterBlock = ^(id target, id source, id json) { \
|
||||
|
||||
// Get value
|
||||
void *value = malloc(typeSignature.methodReturnLength);
|
||||
if (json) {
|
||||
[typeInvocation setArgument:&json atIndex:2];
|
||||
[typeInvocation invoke];
|
||||
[typeInvocation getReturnValue:value];
|
||||
} else {
|
||||
if (!sourceInvocation && source) {
|
||||
NSMethodSignature *signature = [source methodSignatureForSelector:getter];
|
||||
sourceInvocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[sourceInvocation setSelector:getter];
|
||||
}
|
||||
[sourceInvocation invokeWithTarget:source];
|
||||
[sourceInvocation getReturnValue:value];
|
||||
}
|
||||
|
||||
// Set value
|
||||
if (!targetInvocation && target) {
|
||||
NSMethodSignature *signature = [target methodSignatureForSelector:setter];
|
||||
targetInvocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[targetInvocation setSelector:setter];
|
||||
}
|
||||
[targetInvocation setArgument:value atIndex:2];
|
||||
[targetInvocation invokeWithTarget:target];
|
||||
free(value);
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
propBlock = ^(__unused id view, __unused id json) {
|
||||
|
||||
// Follow keypath
|
||||
id target = view;
|
||||
for (NSString *part in parts) {
|
||||
target = [target valueForKey:part];
|
||||
}
|
||||
|
||||
if (json == (id)kCFNull) {
|
||||
|
||||
// Copy default property
|
||||
id source = defaultView;
|
||||
for (NSString *part in parts) {
|
||||
source = [source valueForKey:part];
|
||||
}
|
||||
setterBlock(target, source, nil);
|
||||
|
||||
} else {
|
||||
|
||||
// Set property with json
|
||||
setterBlock(target, nil, json);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (RCT_DEBUG) {
|
||||
|
||||
// Provide more useful log feedback if there's an error
|
||||
RCTPropBlock unwrappedBlock = propBlock;
|
||||
propBlock = ^(id<RCTComponent> view, id json) {
|
||||
NSString *logPrefix = [NSString stringWithFormat:
|
||||
@"Error setting property '%@' of %@ with tag #%@: ",
|
||||
name, weakSelf.name, view.reactTag];
|
||||
|
||||
RCTPerformBlockWithLogPrefix(^{ unwrappedBlock(view, json); }, logPrefix);
|
||||
};
|
||||
}
|
||||
|
||||
propBlocks[name] = [propBlock copy];
|
||||
}
|
||||
return propBlock;
|
||||
}
|
||||
|
||||
- (void)setProps:(NSDictionary *)props forView:(id<RCTComponent>)view
|
||||
{
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_defaultView) {
|
||||
_defaultView = [self createViewWithTag:nil];
|
||||
}
|
||||
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
|
||||
[self propBlockForKey:key defaultView:_defaultView](view, json);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowView *)shadowView
|
||||
{
|
||||
if (!shadowView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_defaultShadowView) {
|
||||
_defaultShadowView = [self createShadowViewWithTag:nil];
|
||||
}
|
||||
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
|
||||
[self propBlockForKey:key defaultView:_defaultShadowView](shadowView, json);
|
||||
}];
|
||||
|
||||
[shadowView updateLayout];
|
||||
}
|
||||
|
||||
- (NSDictionary *)viewConfig
|
||||
{
|
||||
Class managerClass = [_manager class];
|
||||
NSMutableDictionary *propTypes = [[NSMutableDictionary alloc] init];
|
||||
|
||||
unsigned int count = 0;
|
||||
Method *methods = class_copyMethodList(object_getClass(managerClass), &count);
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
Method method = methods[i];
|
||||
SEL selector = method_getName(method);
|
||||
NSString *methodName = NSStringFromSelector(selector);
|
||||
if ([methodName hasPrefix:@"propConfig"]) {
|
||||
NSRange nameRange = [methodName rangeOfString:@"_"];
|
||||
if (nameRange.length) {
|
||||
NSString *name = [methodName substringFromIndex:nameRange.location + 1];
|
||||
NSString *type = ((NSArray *(*)(id, SEL))objc_msgSend)(managerClass, selector)[0];
|
||||
if (RCT_DEBUG && propTypes[name] && ![propTypes[name] isEqualToString:type]) {
|
||||
RCTLogError(@"Property '%@' of component '%@' redefined from '%@' "
|
||||
"to '%@'", name, _name, propTypes[name], type);
|
||||
}
|
||||
propTypes[name] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(methods);
|
||||
|
||||
return propTypes;
|
||||
}
|
||||
|
||||
@end
|
@ -25,6 +25,7 @@
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-initWithFrame:(CGRect)frame)
|
||||
RCT_NOT_IMPLEMENTED(-initWithCoder:coder)
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "Layout.h"
|
||||
#import "RCTViewNodeProtocol.h"
|
||||
#import "RCTComponent.h"
|
||||
|
||||
@class RCTSparseArray;
|
||||
|
||||
@ -32,14 +32,14 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry);
|
||||
* 3. If a node is "computed" and the constraint passed from above is identical to the constraint used to
|
||||
* perform the last computation, we skip laying out the subtree entirely.
|
||||
*/
|
||||
@interface RCTShadowView : NSObject <RCTViewNodeProtocol>
|
||||
@interface RCTShadowView : NSObject <RCTComponent>
|
||||
|
||||
@property (nonatomic, weak, readonly) RCTShadowView *superview;
|
||||
@property (nonatomic, assign, readonly) css_node_t *cssNode;
|
||||
@property (nonatomic, copy) NSString *viewName;
|
||||
@property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children
|
||||
@property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle;
|
||||
@property (nonatomic, assign) BOOL hasOnLayout;
|
||||
@property (nonatomic, assign, getter=hasOnLayout) BOOL onLayout;
|
||||
|
||||
/**
|
||||
* isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is
|
||||
@ -104,7 +104,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry);
|
||||
@property (nonatomic, assign) css_justify_t justifyContent;
|
||||
@property (nonatomic, assign) css_align_t alignSelf;
|
||||
@property (nonatomic, assign) css_align_t alignItems;
|
||||
@property (nonatomic, assign) css_position_type_t positionType;
|
||||
@property (nonatomic, assign) css_position_type_t position;
|
||||
@property (nonatomic, assign) css_wrap_type_t flexWrap;
|
||||
@property (nonatomic, assign) CGFloat flex;
|
||||
|
||||
|
@ -541,7 +541,7 @@ RCT_STYLE_PROPERTY(FlexDirection, flexDirection, flex_direction, css_flex_direct
|
||||
RCT_STYLE_PROPERTY(JustifyContent, justifyContent, justify_content, css_justify_t)
|
||||
RCT_STYLE_PROPERTY(AlignSelf, alignSelf, align_self, css_align_t)
|
||||
RCT_STYLE_PROPERTY(AlignItems, alignItems, align_items, css_align_t)
|
||||
RCT_STYLE_PROPERTY(PositionType, positionType, position_type, css_position_type_t)
|
||||
RCT_STYLE_PROPERTY(Position, position, position_type, css_position_type_t)
|
||||
RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flex_wrap, css_wrap_type_t)
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)color
|
||||
|
@ -21,11 +21,6 @@
|
||||
|
||||
typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *viewRegistry);
|
||||
|
||||
/**
|
||||
* Underlying implementation of RCT_EXPORT_XXX macros. Ignore this.
|
||||
*/
|
||||
RCT_EXTERN void RCTSetViewProperty(NSString *, NSString *, SEL, id, id, id);
|
||||
|
||||
@interface RCTViewManager : NSObject <RCTBridgeModule>
|
||||
|
||||
/**
|
||||
@ -105,35 +100,28 @@ RCT_EXTERN void RCTSetViewProperty(NSString *, NSString *, SEL, id, id, id);
|
||||
/**
|
||||
* This handles the simple case, where JS and native property names match.
|
||||
*/
|
||||
#define RCT_EXPORT_VIEW_PROPERTY(name, type) RCT_REMAP_VIEW_PROPERTY(name, name, type)
|
||||
|
||||
#define RCT_EXPORT_SHADOW_PROPERTY(name, type) RCT_REMAP_SHADOW_PROPERTY(name, name, type)
|
||||
#define RCT_EXPORT_VIEW_PROPERTY(name, type) \
|
||||
+ (NSArray *)propConfig_##name { return @[@#type]; }
|
||||
|
||||
/**
|
||||
* This macro maps a named property on the module to an arbitrary key path
|
||||
* within the view or shadowView.
|
||||
* This macro maps a named property to an arbitrary key path in the view.
|
||||
*/
|
||||
#define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \
|
||||
RCT_CUSTOM_VIEW_PROPERTY(name, type, UIView) { \
|
||||
RCTSetViewProperty(@#name, @#keyPath, @selector(type:), view, defaultView, json); \
|
||||
}
|
||||
|
||||
#define RCT_REMAP_SHADOW_PROPERTY(name, keyPath, type) \
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(name, type, RCTShadowView) { \
|
||||
RCTSetViewProperty(@#name, @#keyPath, @selector(type:), view, defaultView, json); \
|
||||
}
|
||||
#define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \
|
||||
+ (NSArray *)propConfig_##name { return @[@#type, @#keyPath]; }
|
||||
|
||||
/**
|
||||
* These macros can be used when you need to provide custom logic for setting
|
||||
* This macro can be used when you need to provide custom logic for setting
|
||||
* view properties. The macro should be followed by a method body, which can
|
||||
* refer to "json", "view" and "defaultView" to implement the required logic.
|
||||
*/
|
||||
#define RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass) \
|
||||
+ (NSString *)getPropConfigView_##name { return @#type; } \
|
||||
RCT_REMAP_VIEW_PROPERTY(name, __custom__, type) \
|
||||
- (void)set_##name:(id)json forView:(viewClass *)view withDefaultView:(viewClass *)defaultView
|
||||
|
||||
#define RCT_CUSTOM_SHADOW_PROPERTY(name, type, viewClass) \
|
||||
+ (NSString *)getPropConfigShadow_##name { return @#type; } \
|
||||
- (void)set_##name:(id)json forShadowView:(viewClass *)view withDefaultView:(viewClass *)defaultView
|
||||
/**
|
||||
* This macro is used to map properties to the shadow view, instead of the view.
|
||||
*/
|
||||
#define RCT_EXPORT_SHADOW_PROPERTY(name, type) \
|
||||
+ (NSArray *)propConfigShadow_##name { return @[@#type]; }
|
||||
|
||||
@end
|
||||
|
@ -43,15 +43,6 @@ RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
|
||||
|
||||
@end
|
||||
|
||||
void RCTSetViewProperty(NSString *name, NSString *keyPath, SEL type,
|
||||
id view, id defaultView, id json)
|
||||
{
|
||||
if ((json && !RCTSetProperty(view, keyPath, type, json)) ||
|
||||
(!json && !RCTCopyProperty(view, defaultView, keyPath))) {
|
||||
RCTLogError(@"%@ does not have setter for `%@` property", [view class], name);
|
||||
}
|
||||
}
|
||||
|
||||
@implementation RCTViewManager
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@ -273,8 +264,8 @@ RCT_EXPORT_SHADOW_PROPERTY(flexWrap, css_wrap_type_t)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(justifyContent, css_justify_t)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t)
|
||||
RCT_REMAP_SHADOW_PROPERTY(position, positionType, css_position_type_t)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(position, css_position_type_t)
|
||||
|
||||
RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(onLayout, BOOL)
|
||||
|
||||
@end
|
||||
|
@ -9,11 +9,11 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTViewNodeProtocol.h"
|
||||
#import "RCTComponent.h"
|
||||
|
||||
//TODO: let's try to eliminate this category if possible
|
||||
|
||||
@interface UIView (React) <RCTViewNodeProtocol>
|
||||
@interface UIView (React) <RCTComponent>
|
||||
|
||||
/**
|
||||
* Used by the UIIManager to set the view frame.
|
||||
|
Loading…
x
Reference in New Issue
Block a user